Mark
Server Details
Mark runs your content, marketing, and advertising flexibly. Use Mark from your favorite AI assistant or on the platform.
- Status
- Healthy
- Uptime
- 100.0% over the last 24 hours
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- ChatGPT
- –
- Claude
- –
TDQS
Scored across 112 tools
With 112 tools in a content-marketing platform, many pairs have overlapping boundaries: get_post vs show_post vs search_posts, approve_posts vs approve_post_from_preview, save_post vs submit_post vs edit_published_post, get_ai_visibility vs get_buyer_question_answers vs compare_buyer_question_answers, and many near-duplicate 'get_*_performance' tools. Descriptions do try to disambiguate (e.g. show_post draws, get_post does not), but an agent must read long caveats to pick correctly, and several tools exist solely for in-app UI or agent-prohibited paths (get_asset_thumbnails, approve_post_from_preview), muddying selection.
Most names follow a verb_noun pattern (save_post, get_calendar, list_claims, delete_assets), but verbs are inconsistently chosen (save_ vs update_ vs set_ vs add_, run_ vs submit_ vs send_), and there are outliers like show_post, record_post_published, resolve_external_post and check_ai_assistant_tracking. The pattern is readable but not predictable enough to guess a name without the list.
112 tools is an extreme mismatch for a single MCP server and far beyond the 15-tool guideline; the surface spans publishing, CMS, SEO, analytics, brand strategy, competitors, AI visibility, assets, setup and billing. Many tools are thin (dismiss_ai_crawler_404, get_asset_thumbnails, send_setup_checklist, set_business_aliases) and should be consolidated or moved behind a resource/filter parameter.
Coverage is genuinely broad: post lifecycle (draft, submit, approve, review, archive, delete, unpublish, edit published), assets, claims/sources, strategy items, competitors, keyword research, site audits, CMS mapping and performance reporting are all present. Only minor gaps remain (e.g. no bulk approve/edit, limited asset metadata editing), which agents can work around.
Publisher details
- Operator
- Life With Data · Publisher source
- Operator website
- https://lifewithdata.org · Publisher source
- Vendor relationship
- First-party
- Documentation
- https://docs.use-mark.com · Publisher source
- Trust center
- Not applicable
- Restrictions
- Not applicable
Available Tools
112 toolsadd_assetAdd AssetAInspect
Adds an image, video or PDF to the library from a public URL or a file you hold. A PDF becomes kind document and attaches to LinkedIn posts only. For a public https URL, pass source url plus url. For a local file, three steps with any HTTP client: (1) call with source upload and fileName (mime optional) to get a signed uploadUrl, the headers to send, and a pathname; (2) PUT the raw file bytes to uploadUrl with those headers (for example curl -X PUT -T cover.png), within an hour; (3) call again with source upload and that pathname to save it. Mark reads the type and size from storage. For a file behind a login (a private preview), download it with your own access first, then upload it the same way: Mark never takes your credentials. Related: search_assets, save_post.
Inputs: source (required), url, mime, name, +2 more. Returns: asset, upload
add_sourceAdd SourceAInspect
Adds a source to the Claims & sources page: pasted text (call notes, an audit, a case study) with an optional link to the original. Claims quote a source's text, so body is required. Returns the source id. Use it as sourceId in save_claim, or as overlay evidence in update_brand_profile. To add a claim and its source in one call, pass source to save_claim instead. Related: save_claim, list_claims, update_brand_profile.
Inputs: body (required), title (required), url. Returns: appUrl, source
analyze_domainAnalyze DomainAInspect
Looks up any domain (yours or a competitor's): view = overview | top_pages | page_keywords | backlinks. overview, top_pages and page_keywords fetch fresh data from the provider, 1 domain per call. For backlinks, refresh: true fetches a fresh snapshot; otherwise it returns the latest one. Each call spends provider calls, except backlinks without refresh, which reads the stored snapshot. Related: research_keywords, get_rankings.
Inputs: view (required), domain (required), url, siteId, +3 more. Returns: view, result
approve_post_from_previewApprove From PreviewADestructiveInspect
Approves one post at an exact rev. Only the Approve button in Mark's post preview calls this, after a person clicks it. Agents never call it: to approve, use approve_posts, and only when the user has explicitly asked. Related: approve_posts, get_post.
Inputs: rev (required), postId (required). Returns: post, approval
approve_postsApprove PostsADestructiveInspect
Approves the exact revisions given ([{postId, rev}], where rev comes from get_post/search_posts). There is no separate publish step. For website channels the result names the next CMS step (queued at a time, or a CMS draft that does not go live). An approved social post reads approved for a few seconds while Mark hands it to the publisher, then scheduled, and publishes at its scheduled time; confirm with get_post. An approved LinkedIn Article stays approved with handoff publish_by_hand (publish_now once its slot time passes) until someone records its live URL with record_post_published. Each approved outcome carries handoff: handing_off, or stuck with the reason and fix when the post can't go (no scheduled time, no connected account for the channel, or Mark couldn't start the handoff), so you don't have to wait to learn it. A post whose handoff couldn't start can still read handing_off on get_post for up to 5 minutes after approval; trust this result. A stale rev is refused. On a channel set to admins only, only workspace admins can approve; anyone else gets those posts back skipped. Only use it when the user has explicitly asked to approve. Related: get_post, search_posts, review_post.
Inputs: posts (required). Returns: outcomes, submitted, lifecycleWarnings
archive_postArchive PostAIdempotentInspect
Takes a post out of the publish path so it will not go live. The post stays in the workspace; this is not a delete. Posts that are already live, queued to publish, or approved cannot be archived — they stay as they are. Archiving a post that is not a draft (in review, sent back, failed or missed) rejects it, so on a channel set to admins only (approverRole: approver) only workspace admins can archive it; drafts stay open to everyone. Calling it again on a post that will not publish is a no-op. Archiving a website post that sent a preview to its CMS deletes that preview draft when Mark may; otherwise cmsPreview says it is still in the CMS, where, and why, and so does the post's review note. Related: delete_post, review_post, search_posts.
Inputs: postId (required), note. Returns: postId, status, outcome, cmsPreview
check_ai_assistant_trackingCheck AI Assistant TrackingARead-onlyIdempotentInspect
Checks whether ChatGPT, Perplexity and other AI assistants are tracked on a site (probe plus PostHog). Takes up to about 30 seconds. Related: list_sites, get_ai_visibility.
Inputs: siteId (required). Returns: appUrl, snapshot
check_draftCheck DraftARead-onlyIdempotentInspect
Runs Mark's content checks on draft text without saving it (six on every channel, two more on a competitor_page, and one more on a website post with a target keyword): exclusion (phrases this workspace blocks or warns on), char_limit, required_meta, ai_fluff (generic AI-sounding words), unsourced_number (percentages, money and multipliers no active claim states), and placeholder (a marker left for the writer, such as [needs source], [TBD] or a bare TODO, ignored inside code, link targets and URLs; it fails). A competitor_page also gets table (a markdown table; it fails, use headings and lists) and open_placeholder (a [confirm: …] fact left to confirm; it fails), and its unsourced_number fails instead of warning. A blog, faq or competitor_page with meta.seo.keyword set also gets keyword (whether seo.title and the body's first paragraph use the target keyword, plurals counted; it only warns, naming each place it is missing). Each result is pass, warn or fail with the matched text, field and position; exclusion matches carry the rule's reason. A fail is what submit_post refuses on a draft; warnings never block. For an X thread, pass assetIds to check each post's media too. On a blog, faq or competitor_page whose post type a CMS maps, required_meta also checks language (pass the post's language): it fails when the CMS collection stores pages per language (get_website_cms localized) and warns otherwise. Uses this workspace's active exclusions and claims. Writes nothing. Related: save_post, get_channel_rules, get_brand_context, list_claims, get_website_cms.
Inputs: body (required), channel (required), meta, assetIds, +1 more. Returns: checks
check_keyword_audienceCheck Keyword AudienceAInspect
Looks at who currently ranks for a keyword, to judge whether its searchers match your audience. Each call is 1 search: it fetches the current Google top 10. Each call spends provider calls. Pass a savedKeywordId from list_saved_keywords, or a plain keyword to look it up before saving: that saves and tracks nothing. A plain keyword uses the priority market unless you pass both locationCode and languageCode. Related: save_keywords, research_keywords.
Inputs: keyword, languageCode, locationCode, savedKeywordId. Returns: summary, checkedAt
check_site_readinessCheck Site ReadinessBRead-onlyIdempotentInspect
Re-scans a site for AI-agent readiness (robots, llms.txt, structured data, speed). Related: list_sites, get_ai_visibility.
Inputs: siteId (required). Returns: appUrl, reason, report, outcome
comment_on_postComment on PostAInspect
Adds a review comment to a post's current revision, visible to your team in Mark. To comment on part of the post, start the body with that text quoted, each line prefixed "> ", then a blank line, as Mark's chat preview does. Does not change the post's status. Related: get_post, review_post.
Inputs: body (required), postId (required). Returns: appUrl, postId
compare_buyer_question_answersCompare Buyer Question AnswersARead-onlyIdempotentInspect
Two stored check runs of one buyer question in one market, side by side per engine: each side's answer text, whether it cited or named you (youNamed: your current business name in the text), which tracked competitors it named or cited, and its cited pages with their stored titles; plus the pages cited in After and not Before (added), the reverse (dropped), and a count cited in both. Pages match ignoring www., case of the host and a trailing slash. By default After is the newest run and Before is the last run before the question's live answer page was last published or pushed to the CMS (else the previous run); defaultReason says which. Lists every completed or partial run you can pass, same-week reruns included. Reads stored checks only. Related: get_buyer_question_answers, get_ai_visibility.
Inputs: question (required), market, afterRunId, beforeRunId. Returns: found, appUrl, reason, comparison
delete_assetsDelete AssetsADestructiveInspect
Permanently deletes assets. With removeFromDrafts: true, it also removes them from editable posts; published posts keep their copy. This cannot be undone. Related: get_asset, search_assets.
Inputs: assetIds (required), removeFromDrafts. Returns: results
delete_postDelete PostADestructiveInspect
Permanently deletes a post: a draft, one in review, one approved or scheduled, or a published one. An unpublished post's Zernio post is canceled and deleted first (for a website post, its pending CMS publish is withdrawn); if that fails, nothing changes. Its review history, comments and performance logs are deleted with it; tracking links stay and keep their clicks. External posts cannot be deleted. A website post's preview draft in its CMS is deleted first when Mark may; otherwise cmsPreview says it is still in the CMS, where, and why, so you can delete it there. Published posts are admins only; anyone else is refused. By default a published post is removed from Mark only and stays live (takenDown left_live with its liveUrl). With takeDown: true, Mark first deletes it from LinkedIn, X, Facebook, YouTube or Pinterest, or unpublishes a website page in its CMS and then deletes the page when Mark's key may (otherwise takenDown left_in_cms says where the draft stays). Instagram and TikTok posts can't be taken down by apps: takeDown is refused for them, so remove them in the app and delete with takeDown false. A failed take-down changes nothing: the post stays in Mark, still published, and the error names the platform and the fix. This cannot be undone. Related: archive_post, search_posts, save_post.
Inputs: postId (required), takeDown. Returns: postId, status, outcome, takenDown, +1 more
delete_siteDelete SiteADestructiveInspect
Permanently removes a website and its audits and readiness history. The site a website CMS publishes to (get_website_cms site) is refused until the CMS is disconnected or moved to another site in the Mark app. This cannot be undone. Related: list_sites, save_site, get_website_cms.
Inputs: siteId (required). Returns: siteId
delete_sourceDelete SourceADestructiveInspect
Deletes a pasted note (a source with type note) from the Claims & sources page. It also leaves the source picker. Cannot be undone. Refused while any claim quotes the note, active or retired (list_claims shows claims per source), or while brand profile evidence cites it (update_brand_profile). Other source types cannot be deleted here. Related: list_claims, update_source.
Inputs: id (required). Returns: appUrl, deletedId
delete_strategy_itemDelete Strategy ItemADestructiveInspect
Permanently deletes a persona, pillar, angle, market or KPI target, or deletes a buyer question. A deleted buyer question leaves every list and future AI answer checks, but its past answers and wrong answers stay, and saving the same text again with save_strategy_item brings it back. To pause a question instead, call save_strategy_item with active: false. Other deletes cannot be undone. An item still in use is refused: a persona or pillar that angles use (the error names them, so delete or move those angles first), anything posts use, or a market that answer-engine checks or monthly AI-visibility results use. To retire an item that is in use, call save_strategy_item with its id and active: false. Related: save_strategy_item, get_brand_context.
Inputs: id (required), kind (required). Returns: id, kind, appUrl, result
disconnect_connectionDisconnect AccountADestructiveInspect
Disconnects an integration. With zernio (Social publishing) this disconnects every social account at once, not one channel, and no scheduled social post will publish until each account is reconnected in the Mark app. To see which social accounts would go, call list_channels first. For a website CMS, the reply names the provider and warns when channels are still mapped to it; leftover queued publishes then fail until those channels are mapped again. Connecting accounts cannot be done from the agent. Related: list_connections, list_channels, get_website_cms.
Inputs: connectionId (required). Returns: connectionId
dismiss_ai_crawler_404Dismiss AI Crawler 404AIdempotentInspect
Hides one path from get_site_audit's aiCrawler404s and the Site health list for 90 days. Dismissing it again restarts the 90 days. If the path still answers 404 when the dismissal ends, it shows again. Changes only Mark, never the site. Related: get_site_audit.
Inputs: path (required), siteId (required). Returns: appUrl, expiresAt
draft_competitor_pageDraft Competitor PageAIdempotentInspect
Drafts a comparison page for one tracked competitor as a competitor_page draft post: kind vs (" vs ", which also answers " alternative" searches) or pricing (" pricing", what they charge and how you compare). There is no alternative kind: kind alternative is refused, so draft the vs page. One AI model call writes it from Mark's facts about them (price claims sourced on their own site, this week's AI answers that named them and not you, their ranked keywords). Each fact gets a numbered marker, and Mark adds a Sources section with each source and its checked date. A fact Mark has no source for is a [confirm: …] placeholder; every price is one when no price claim is sourced on their site (add one with add_source and save_claim). No tables. When that competitor already has a live (not archived) post of that kind, returns it and writes nothing; markAppUrl opens the editor, or the review page once it is past draft. Refuses an unknown or untracked competitor, and one whose name or domain matches a content exclusion. A blocked phrase in the generated text saves nothing. Never submits: open placeholders, tables and numbers no active claim states fail the content checks, so fix them, then call submit_post. Takes up to a minute. Related: list_competitors, get_competitor_scorecard, check_draft, save_post, submit_post.
Inputs: kind (required), competitorId (required). Returns: postId, status, created, markAppUrl, +1 more
edit_published_postEdit Published PostAInspect
Replaces the text of a published social post on its platform, right away. The new text goes live as soon as the call returns; it does not go back through review, and it is recorded as a new rev approved by you. Only people who can approve the channel's posts may use it: on a channel set to admins only (approverRole: approver), workspace admins. Text only: media, the first comment, YouTube and Pinterest titles, and X thread replies stay as posted. Works on LinkedIn, Facebook, X, YouTube (the description) and Pinterest (the description, if Pinterest lets apps edit pins). X: single posts only, within 1 hour of going live, up to 5 edits, and the account needs X Premium; X gives the edited post a new address, returned as url. Instagram and TikTok are refused: edit those in their apps. The text gets the same checks as save_post (the channel's character limit and blocked phrases); blank text is refused. Links go out as written, so keep the tracking links from the live text. Drafts, posts not yet published, website pages and external posts are refused, and so is a second edit of the same post while one is in progress. A refusal changes nothing in Mark or on the platform, and the error names the platform and its reason. Two errors are different: if the error says Mark couldn't confirm the platform took the edit, check the live post before calling again; if it says the live post changed but Mark didn't record it, call again with the same text. Related: get_post, save_post, delete_post.
Inputs: body (required), postId (required). Returns: rev, url, postId, channel
estimate_ai_volumeEstimate AI Search VolumeARead-onlyIdempotentInspect
Estimates how often each of 1 to 50 phrases is asked in AI assistants each month, in every active market with a location and language, without saving anything. Use it to vet a question before adding it as a buyer question. Results come back in input order, each with aiSearchVolume, the total across those markets (null when no market has an estimate), and byMarket, one row per market, highest first. The total adds up markets in different languages: read it as how widely a question is asked, not one market's demand. This is AI search volume, not the Google search volume research_keywords returns. If this refuses because the workspace is not set up, call get_setup_status and check its rows, including skipped ones. Add a phrase with save_strategy_item (kind buyer_question). Related: refresh_ai_volume, get_ai_visibility, set_up_market.
Inputs: phrases (required). Returns: scope, results
export_context_packExport Context PackARead-onlyIdempotentInspect
Exports this workspace's context pack: four markdown files (company, customer, offer, voice) built from what is saved in Mark. company has positioning, differentiators, doctrine, company facts and entities; customer has the active personas with pains, jobs, objections and their words; offer has what is sold today, what is planned, pricing, promises and claims to avoid; voice has tone, do and don't lines, example posts, posts that don't sound like the brand, and active content exclusions. Each file opens with the workspace name and export date, and marks a Brand section that is not confirmed. The files use the layout brand-interview reads, so they can seed another folder or workspace. Edits belong in Mark, not in the files. Does not change anything. Related: get_brand_context, update_brand_profile, save_strategy_item.
Inputs: no named parameters. Returns: files, appUrl, exportedAt, workspaceName
get_ai_visibilityGet AI VisibilityARead-onlyIdempotentInspect
Shows how AI assistants see you: buyer questions (each with answerPages: the website posts linked to it with save_post buyerQuestionIds, label, status, live URL; empty means no page answers it yet), suggestions from People Also Ask, Search Console question searches and Reddit threads Google showed for audience-checked saved keywords (redditSuggestions: question, url, subreddit, keywords, total), AI search volume on each buyer question (aiSearchVolume: the monthly estimate summed across every active market with a location and language, null when none has one; aiSearchVolumeByMarket: marketId, market, country, aiSearchVolume, fetchedAt per market, highest first, then markets with no data, then markets not fetched yet with fetchedAt null; aiSearchVolumeFetchedAt: the newest fetch), traffic: pages AI assistants' bots fetched (not referrals or citations), where each bot row's hits split into verified (IP on the vendor's published list), checkable minus verified (failed the check) and hits minus checkable (no list, list not loaded, or no IP), and paths are the top 10 pages without fonts, images, styles or scripts, each with lastStatus: the HTTP status of Mark's latest daily check of that path on the primary site (404 means AI assistants can't find it; see get_site_audit aiCrawler404s), or null when unchecked or unknown; traffic.state is ready, no-posthog, no-signal (PostHog has received no events yet) or failed, and a failed read has reason: busy means PostHog didn't answer in time (it runs 3 queries at once per project and queues the rest), so try again in a minute; the PostHog connection is fine. error is any other failure, such as lost access, so check the PostHog connection. verificationSetup: the last result of Mark installing its IP check in your PostHog project (status ok or failed, reason limit, access or error, checkedAt; null before the first run or with no project), readiness, and AI answers: for the priority market's latest weekly check, how often ChatGPT, Gemini, AI Overview, AI Mode, Claude and Perplexity named or cited you (Claude and Perplexity are asked through their APIs with web search on, not the consumer apps, so their answers can differ from what people see in the apps), the top domains they cited, and up to 5 suggested competitors from the third-party sites they cite most (null with a reason when there is nothing yet), plus shareOfVoice: your share of mentions among you and your tracked competitors per engine in that run, and your share over the last 12 weekly runs, and nextAnswerCheck: how many checks the next weekly AI answer check sends (active questions × markets with a location and language × engines), the line Settings > Workspace shows. Does not change anything. Add any suggestion with save_strategy_item (kind buyer_question, fields.question). Related: refresh_ai_volume, save_strategy_item, check_site_readiness.
Inputs: no named parameters. Returns: appUrl, traffic, readiness, suggestions, +7 more
get_assetGet AssetARead-onlyIdempotentInspect
Opens one asset: preview URL, details, and which posts use it. Related: search_assets, update_asset, delete_assets.
Inputs: assetId (required). Returns: asset, usage
get_asset_thumbnailsAsset ThumbnailsARead-onlyIdempotentInspect
Signed URLs, valid one hour, for image assets in this workspace. Only Mark's calendar widget calls this to draw thumbnails. Agents never call it. Related: get_calendar.
Inputs: assetIds (required). Returns: urls
get_brand_contextGet Brand ContextARead-onlyIdempotentInspect
Returns everything that shapes content: the Brand page's four sections (profile.voice, profile.visual, profile.overlay for entities and evidence, profile.offer for what is sold today, what is planned, pricing, promises and claims to avoid; profile.voiceError, visualError, overlayError or offerError explains a stored section that needs repair), personas (with wants, convincedBy, objections, pains, jobs and language: the customer's own words), pillars, angles, markets, buyer questions, KPI targets (for their actuals and progress, call get_content_performance), and active content exclusions. Call it before drafting or reviewing. brandReview says, per Brand section, whether a person has confirmed it (confirmed, confirmedAt) or it needs repair; tell the person which sections still need their review. claims lists the active claims (id, text, category, sourceQuote, sourceId, sourceTitle): the only checkable facts (numbers, prices, results, certifications) drafts may state. category is the workspace's own label. Retired claims are left out; for them and for search, call list_claims. sources lists the Claims & sources entries (id, title, url) that overlay evidence and claims cite by sourceId. profile.visual.logoAssetId is an asset id; call get_asset for the image. organizationJsonLd is the ready-to-paste Organization JSON-LD script built from the saved primary entity, company site, sameAs and Wikidata ids (null when there are no sameAs URLs or Wikidata ids), plus logo when the Brand logo was imported from a public URL (uploaded and Drive logos have no public URL, so logo is left out); it goes in the head of the person's home page. Personas, pillars, angles and markets include inactive rows (active: false). Each angle is one persona × pillar pair: it carries personaId and pillarId plus personaName and pillarName, so group angles by those to see which pairs have one. People edit personas, pillars and angles at /messaging, and markets at /markets. A market with null locationCode or languageCode has no search location, so AI search volume and answer-engine checks skip it. Does not change anything. Related: update_brand_profile, save_strategy_item, save_content_exclusion, list_claims, save_claim, get_channel_rules.
Inputs: no named parameters. Returns: angles, appUrl, claims, markets, +9 more
get_buyer_question_answersGet Buyer Question AnswersARead-onlyIdempotentInspect
One buyer question's stored AI answers for one market and week: its answer pages (website posts linked to the question, whether each is live, and which engines cited it that week), per engine (ChatGPT, Gemini, AI Overview, AI Mode, Claude, Perplexity; Claude and Perplexity are asked through their APIs with web search on, not the consumer apps, so answers can differ from the apps) the answer text, whether it cited or named you (youNamed: your current business name in the text), which tracked competitors it named or cited (the names the app highlights in the answer), and the cited pages marked yours or a competitor's with their stored titles, plus each engine's baseline (its first answered check in this market). Also lists the weeks you can pick. Reads stored weekly checks only. Related: compare_buyer_question_answers, get_ai_visibility, get_competitor_ai_detail, save_wrong_answer.
Inputs: question (required), market, weekKey. Returns: found, appUrl, reason, answers
get_calendarGet CalendarARead-onlyIdempotentInspect
Shows the publishing calendar for one week: each day's scheduled and published posts in time order, the open posting slot positions to fill, and how many of each channel's slot posts are filled. A slot is a weekday and a number of posts that day; each open position has its slot id and local date (which reschedule_post takes to fill it), its time (the channel's posting time plus 3 hours per post already that day, or now once that has passed today) and filled out of count. An approved social post's card carries handoff, as in get_post: handing_off before it reads scheduled, or stuck with the reason and fix; an approved LinkedIn Article's reads publish_by_hand, or publish_now once its time has passed. Each post card carries its pillar name (pillar, null when it has none) and pillarId, so you can filter a week by pillar or channel yourself. Pass week as any date in it (YYYY-MM-DD); for a month, call it once per week. Times render in the workspace timezone, or in timezone if you pass an IANA name (render only; stored data does not change). Related: save_post, reschedule_post, update_posting_slots.
Inputs: from, week, timezone. Returns: calendar
get_channel_rulesGet Channel RulesARead-onlyIdempotentInspect
Gives the format rules for a channel (length limits, accepted media ratios, meta field names and types), from the same source Mark's in-app writer uses. For X it says that each link counts as 23. For linkedin_article (a long-form LinkedIn Article) it gives title (required) and cover (optional) over a Markdown body; Mark doesn't publish Articles, a person does. For writing craft and this workspace's rules, call get_drafting_guide. For blog, faq and competitor_page it also reports the website CMS mapping in force (collection, required fields, body format, cover rule, URL pattern), whether a CMS is connected, and whether approval saves a CMS draft or publishes live. Omit channel to list the supported channels. Related: list_channels, save_post, get_brand_context, get_website_cms.
Inputs: channel. Returns: rules, channels
get_competitor_ai_detailGet Competitor AI DetailARead-onlyIdempotentInspect
One tracked competitor against you in this week's AI answers, by engine (ChatGPT, Gemini, AI Overview, AI Mode, Claude, Perplexity; Claude and Perplexity are asked through their APIs with web search on, not the consumer apps): how often each is named and cited, the buyer questions where an AI named them and no AI named you (with the stored answers), and the sites AI cited for them in answers that cite none of your sites. Related: get_competitor_scorecard.
Inputs: competitorId (required), languageCode, locationCode. Returns: detail
get_competitor_scorecardGet Competitor ScorecardARead-onlyIdempotentInspect
Your sites and each tracked competitor side by side for one market: how often this week's AI answers to your buyer questions name and cite each brand (count of answers), organic traffic and keywords, and monthly share of ChatGPT and AI Overview mentions. These cover your own buyer questions, not the whole market. Null means no answers or no data yet. nextComparisonPage is the comparison page to draft next ({ competitorId, name, kind, namedNotYou }, the Competitors page's card), or null when nothing is left: for this market, the tracked competitor (not one a content exclusion names) AI answers name most often instead of you, then the one ranking for the most keywords, vs page first, then pricing only once a price claim is sourced on their site. Related: get_competitor_ai_detail, list_competitors, draft_competitor_page.
Inputs: languageCode, locationCode. Returns: appUrl, scorecard, nextComparisonPage
get_connect_linkGet Connect LinkARead-onlyIdempotentInspect
Returns the in-app URL to connect a provider (the same ids list_connections uses, or a channel such as linkedin). The link is for this workspace only and never includes tokens or secrets. Connecting accounts and pasting API keys still happens in the Mark app. After the user clicks the link, call sync_connections. For a social channel, list_channels then shows whether the account is connected. Related: list_channels, list_connections, get_setup_status, sync_connections.
Inputs: provider (required). Returns: name, appUrl, uiOnly, provider, +1 more
get_content_performanceGet Content PerformanceARead-onlyIdempotentInspect
Reports how content performed over a period, the numbers Performance › Social shows: performance.rows, one per pillar plus No pillar and Total, each with its posts in postRows. impressions and engagement are what each social post gained on days in the period, wherever it was published, so an older post still gaining counts (its postRows entry has publishedBeforeRange true); clicks are tracked-link clicks in the period. pageviews is a blog, FAQ or competitor page's views on the period's days, from the site tag in PostHog, from every country even with marketId, each day counted once it's over in the workspace time zone; 0 for social posts and without PostHog; a row whose posts are all social reads 0, which the app shows as n/a since social posts have no pageviews. posts counts posts published in the period, measured those with a gain dated in it, or for a website post Search numbers or a pageview day in it; an older website post with pageviews in the period is listed with publishedBeforeRange true. performance.daily is every day of the period with the impressions and engagement social posts gained on it, 0 on a day with none (blog Search numbers are only in the rows). previous has each row's impressions, engagement, clicks and pageviews, and its daily, for the same number of days just before. Mark reads a post daily to day 30, then weekly to day 90, and spreads a gain evenly over the days between readings; posts read only at day 1 and day 7 show nothing after day 7. A blog or other website post's impressions and clicks are Google Search's for its page in the period, from Search Console (with marketId, only searches from that market's country when it is an ISO code, as get_performance_overview and Performance › Search filter), with no engagement; KPI actuals leave them and pageviews out. kpis is the Targets page (/targets): every KPI target with its month (YYYY-MM), target, actual and progressPercent, computed the same way the page does. Impressions and engagement_rate actuals are what social posts gained on days in the target's month, wherever they were published, the same as performance for that month without blog Search numbers; posts_per_week counts posts published in it and leads those created in it; all in the workspace time zone; it ignores from, to, pillarId and marketId. period is past, current (actual so far) or not_started (actual null). When there is no number, actual is { note } with the reason, such as a metric Mark doesn't track yet. engagement_rate values are percents (4.5 means 4.5%). Does not change anything. Related: save_strategy_item (kind kpi_target), delete_strategy_item, get_search_performance, get_calendar, search_posts.
Inputs: to, from, marketId, pillarId. Returns: kpis, appUrl, previous, performance
get_drafting_guideGet Drafting GuideARead-onlyIdempotentInspect
Returns the drafting guide for one channel: the honesty rules every draft follows, the channel's craft, general writing craft (plus AEO/GEO writing on blog, faq and competitor_page, and a visual brief on instagram, tiktok, youtube and pinterest: shot list, on-screen text, thumbnail and script), when a post needs media and where to find it, design craft for a new graphic, this workspace's brand voice and its colors and fonts, content exclusions and notes for the channel, and the platform limits. Check a draft against it before saving, then pass its guideVersion to save_post. Claims and sources are not in the guide: call get_brand_context for those. Does not change anything. Related: save_post, get_brand_context, get_channel_rules.
Inputs: surface (required). Returns: limits, surface, sections, guideVersion
get_ga4_organic_overviewGet GA4 Organic OverviewARead-onlyIdempotentInspect
Reports Google Analytics organic search sessions, active users, and key events for a date range against the same number of days just before it, plus the top organic landing pages. Requires a connected Google Analytics property. Does not change anything. Related: get_search_performance, get_content_performance.
Inputs: to, from. Returns: rows, appUrl, current, previous, +2 more
get_page_detailGet Page DetailARead-onlyIdempotentInspect
Reports everything Mark knows about one page URL for a date range: Search Console clicks, impressions, CTR, position and top queries for that page; tracked keywords whose latest rank is that page; its issues in the latest site audit; Mark posts published at it or linking to it; first-touch leads that landed on it, with conversion rate and the Top pages Leak or Write more signal; site tag leads (lead_submitted events in the workspace's PostHog project whose visit started on it, shown beside leads and never added to them; siteTagLeads.state is ready, no-posthog, no-signal or failed, with reason: busy means PostHog didn't answer in time (it runs 3 queries at once per project and queues the rest), so try again in a minute; the PostHog connection is fine. error is any other failure, such as lost access, so check the PostHog connection.); and buyer questions whose AI answers cited it. Each block says why when it has no data. Pass the page URL exactly as get_search_performance returns it, scheme included, or Search Console may find no rows for it. The URL must be on this workspace's Search Console property, or on one of its sites with none. Does not change anything. Related: get_search_performance, get_site_audit, get_rankings, get_ai_visibility.
Inputs: url (required), to, from, marketId, +2 more. Returns: url, appUrl, blocks
get_performance_overviewGet Performance OverviewARead-onlyIdempotentInspect
Reports the Performance page's Overview tab for a date range: one row per funnel stage, each the same number its tab shows. Visibility is Search Console impressions; Visits are Search Console clicks and PostHog site pageviews; AI answers is the Named rate per engine from the latest weekly check in the market (no compare, no target, never one blended number); Leads are first-touch leads Mark recorded, counted on the Pacific Time days Search Console reads. Each row has stage, metric, value, previous and changePercent (with compare), target, dataThrough, connection (ready, or why there is no number, such as not_connected, no_property, no-posthog, no-signal or failed) and appUrl, the tab it opens. A failed Site pageviews row has failure: busy means PostHog didn't answer in time (it runs 3 queries at once per project and queues the rest), so try again in a minute; the PostHog connection is fine. error is any other failure, such as lost access, so check the PostHog connection. target is { month, target, actual, progressPercent }, exactly as the Targets page and get_content_performance compute it, and only when from to to is one target month (a whole past month, or this month from the 1st to today); today only leads have a matching KPI metric. compare adds the window of the same length just before. Does not change anything. Related: get_search_performance, get_visitor_locations, get_ai_visibility, get_content_performance.
Inputs: to, from, compare, marketId. Returns: to, from, rows, appUrl, +4 more
get_plan_usageGet Plan UsageARead-onlyIdempotentInspect
Shows this workspace's Mark plan (none, starter, growth, enterprise), subscription status, billing interval, trial end and current period end, and the 4 plan caps with used and limit: sites, trackedKeywords, aiPrompts (active buyer questions times configured active markets) and socialAccounts. limit is null when nothing limits that cap. These are the numbers Settings > Billing shows; the card on file shows only there. Starting a trial, paying, upgrading or changing the card happens only in the Mark app: billingUrl opens Settings > Billing, and upgradeUrl is set when a bigger plan is available. Does not change anything. Related: get_workspace.
Inputs: no named parameters. Returns: caps, plan, status, billingUrl, +4 more
get_postGet PostARead-onlyIdempotentInspect
Use this when you need one post's details to act on it: to edit it, check its media or checks, approve or review it at its current rev, or read several posts for a task. It returns data and draws nothing; when the person wants to see or review a post themselves, use show_post. Returns one post: body, channel fields, media (asset ids and names; on an X thread, meta.thread holds the replies and meta.threadMedia the asset ids on each reply, in the same order, with post 1 holding the rest; a social post's images from the CMS are meta.cmsImages, and meta.mediaOrder, when set, is the order of post 1's media across both), schedule, status, updatedAt, claim flags, comments (oldest first; each adds author, the display name or the raw id when the name cannot be resolved, and via, the client it was posted through or null), approval history, who approved it (approvedBy: { id, name, at }; name is "Mark (automatic)" for auto-approval and the raw id when the name cannot be resolved; null unless the post is approved, scheduled or published), the live URL (externalUrl), the reason it failed or was not sent (reviewNote), the handoff of an approved social post to the publisher (handoff: { state: handing_off } for the few seconds before it reads scheduled, { state: stuck, reason } when it can't go, with the fix: no scheduled time, no connected account for the channel, or no progress 5 minutes after approval; on an approved LinkedIn Article { state: publish_by_hand, reason } before its scheduled time and { state: publish_now, reason } from it on; on an approved website post whose post type no CMS maps { state: publish_by_hand, reason }, recorded with record_post_published once it is live on the site; null otherwise), the buyer questions a website post answers (buyerQuestions: id, question), the idea it was made for (idea: id, title, archived; null when none), a website post's language (language: a BCP 47 code such as de-CH, the locale its CMS page is saved in; null when unset), the post it translates (translationOf: id, language, title; null when it is not a translation) and the posts that translate it (translations: id, language, title), and, on a refresh of a live page Mark published, refresh: { text, pageUrl } with the reason Mark queued it (null otherwise; approving updates that same page). A published social post carries metricHistory: every metrics reading, oldest first, as { date (YYYY-MM-DD in the render timezone), day (whole days since publish), impressions, engagement }, the newest matching get_content_performance; impressions is the platform's impressions count and engagement is likes + comments + shares + saves, each the total the platform reported at that reading ([] before the first reading; null on unpublished posts). A published blog, FAQ or competitor post's metricHistory is instead its daily site-tag readings, oldest first, as { date (YYYY-MM-DD, the workspace calendar day it covers, whatever timezone is passed), pageviews, submissions (form submissions whose visit started on the page) }, with dataThrough (the newest reading's date, null before the first) and connection (ready, or no-posthog when the workspace has no PostHog project of its own; metricHistory is then [] and the person connects PostHog under Connections); [] before the first reading. Blog, FAQ and competitor posts also carry website: where the page goes, its delivery state (preview, queued, sent_to_cms, published, taken_down, failed, removed, conflict, out_of_sync), the page URL, the draft preview link on the tenant's site (previewUrl) with previewSentAt and previewStale for a pre-approval preview, previewLeftAt (set when the post was rejected or archived and Mark left that preview draft in the CMS; reviewNote says why), previewBlocked (why send_cms_preview is refused, or null), a failed job id to pass to sync_connections retryJobId, a live retry error when the job is still pending, and the exact document Mark sends to the CMS. conflict means the CMS page changed since Mark last sent it and needs an explicit overwrite-or-keep choice; conflictDiff then lists each changed field with its CMS value (cms) and Mark's value (mark); choose with resolve_cms_conflict. checks runs Mark's content checks on the current body, meta and language (exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder; table and open_placeholder on a competitor_page; keyword on a website post with meta.seo.keyword). revisionChecks are the results Mark stored for the current rev when it was sent to review, the ones the reviewer sees ([] at rev 0 or when Mark didn't check that revision); they include brand_fit (the brand voice and offer check, warn or pass) once it lands, up to a minute after submit, and never when the workspace's intelligence is off or its voice is unconfirmed. rev is the frozen revision. It advances on submit, and when save_post edits a post in review, approved or scheduled; saving a draft leaves it alone, and updatedAt confirms that save. Call this before approve_posts, review_post, or a save_post edit to a post in review, so you have the current rev. Optional timezone (IANA) renders scheduled and published times in that zone; it does not change stored data. Related: show_post, search_posts, save_post, approve_posts.
Inputs: postId (required), timezone. Returns: post
get_rankingsGet RankingsARead-onlyIdempotentInspect
Shows tracked keyword positions now and over time, with winners and losers. Related: list_saved_keywords, save_keywords.
Inputs: siteId. Returns: appUrl, history, overview
get_search_performanceGet Search PerformanceARead-onlyIdempotentInspect
Reports Google Search Console results (queries, pages, clicks, impressions, position), including striking distance queries (positions 5–20) and anomalies. The text repeats totals and the top 10 pages, queries, striking distance rows and anomalies as markdown and as a fenced JSON block. Pass compareFrom and compareTo for prior-period totals. performance.daily, totals and dataThrough are Google's final data only; performance.preliminary has the newer days Google may still revise, the same shape as daily, never in daily or totals. The default period ends 3 days ago, so preliminary is usually empty; pass to as today's date for Google's newer days. Requires a connected Search Console property. Related: inspect_url, list_saved_keywords.
Inputs: to, from, compareTo, compareFrom. Returns: appUrl, exceptions, performance
get_setup_statusGet Setup StatusARead-onlyIdempotentInspect
The workspace's setup checklist, the same rows in the same order as the setup card on Home and the setup email: website, market, brand voice, web analytics, content sources, social accounts, buyer questions, posting times, connect an agent. Each row has state done, todo or skipped and an in-app action URL. gaps lists the todo rows, each fix naming the tool to call or the page to open. The social row is done once a social account is connected; if Social publishing then needs attention, gaps adds one social entry with the reason and when it was last checked. ready is true once every row is done or skipped and no such entry is open, so it does not promise that drafting or research will work: a skipped market still blocks keyword research. Does not change anything. Related: skip_setup_step, set_up_market, save_site, get_connect_link, update_posting_slots.
Inputs: no named parameters. Returns: gaps, rows, ready, appUrl
get_site_auditGet Site AuditARead-onlyIdempotentInspect
Returns the latest site audit: score, issues by severity with affected pages, the prioritized fix plan, and aiCrawlerAccess: what the site's robots.txt says to each AI crawler (allowed, blocked, no rule, or unknown), not whether an edge or WAF lets it through. Each page carries render: how a crawler that runs no JavaScript reads it, as class js_heavy (needs JavaScript), static_html (pre-built HTML) or raw_html (content in the HTML, no sign it was pre-built), or null with why when its raw fetch failed, plus keyPage (homepage, pricing, compare, demo, contact and similar); a js_heavy key page is a fix candidate. Sites that serve AI bots pre-rendered HTML by user agent (dynamic rendering) still read as js_heavy, because Mark fetches with its own user agent. Four checks are recommendations that do not change the score: article_schema_incomplete (a post's Article JSON-LD lacks recommended fields such as a Person author), schema_only_after_js (JSON-LD that appears only after JavaScript runs), ai_crawler_blocked (robots.txt blocks an AI search or user-fetch bot) and hub_missing (the homepage header and footer miss one or more of these sections: features or services, pricing, compare, resources, and company; detail.missing lists them). Two site-structure checks do count: money_page_few_links (a pricing, feature, service, product or comparison page with fewer than 2 internal links; detail.inboundLinks) and standard_page_missing (no Privacy, Terms, or Contact or Support page, or the one found returns 4xx or 5xx or has under 50 words; detail.missing and detail.broken). agenticReadiness holds Google Lighthouse's experimental agentic browsing checks (Chrome 150+, may change), run through PageSpeed Insights on up to three pages: each check is pass, fail, na, informative or error (couldn't check), read as N of M passed over pass and fail only, never a score; llms.txt is Mark's own fetch by Lighthouse's rules; markdown (what Accept: text/markdown returns) and links (the homepage's Link header) are Mark's facts, not scored; a failing check is also an informational agentic_* issue. With saved identity links, the incomplete Organization schema finding points (appUrl) at the Brand page's Organization JSON-LD, which get_brand_context returns as organizationJsonLd. aiCrawler404s (primary site with PostHog connected, else empty) lists pages AI assistants can't find: paths assistants fetched 3+ times in 7 days, over 2+ days or from 2+ assistants, that answered 404 on Mark's daily check, each with hits, assistants, checkedAt and a recommendation: redirect (a 301 to url, with the matchedTerms), keep_404 (no confident match; redirecting to an unrelated page is a soft 404), no_audit (run an audit for a suggestion) or publish_file (robots.txt, a sitemap, llms.txt or /.well-known/: publish the file rather than redirect). Dismissed paths are left out. A site with no audit yet returns audit null when it has aiCrawler404s, and an error otherwise. Poll this after run_site_audit. Related: run_site_audit, check_site_readiness, get_brand_context, dismiss_ai_crawler_404.
Inputs: siteId (required). Returns: audit, appUrl, aiCrawler404s
get_visitor_locationsGet Visitor LocationsARead-onlyIdempotentInspect
Where your site's visitors were over a period, by country, from your PostHog project: the Performance page's Visitors tab. A visitor is a PostHog person; countries are every located country, most visitors first, each with its ISO alpha-2 code, name, visitors and pageviews. unknown counts visitors PostHog couldn't place (GeoIP off, a reverse proxy that hides visitor IPs, or cookieless mode, where PostHog drops the IP); unknown.cookieless is how many of them came in cookieless mode. total sums the rows, so someone seen in two countries counts twice. state is ready, no-posthog (not connected), no-signal (PostHog has received no events yet) or failed, with reason: busy means PostHog didn't answer in time (it runs 3 queries at once per project and queues the rest), so try again in a minute; the PostHog connection is fine. error is any other failure, such as lost access, so check the PostHog connection. source ai_referral keeps only visits opened from an AI assistant's answer (ChatGPT, Perplexity, Claude, Gemini, Copilot and others), matched by referring domain or utm_source: the AI referrals card on the AI assistants tab. It is a floor: assistant clicks that arrive with no referrer and no utm_source can't be counted. Rank AI referrals by visitors, not pageviews: on a single-page site every pageview of the visit keeps the AI referrer, on a multi-page site only the first does. Does not change anything. Related: get_ai_visibility, get_content_performance, get_setup_status.
Inputs: to, from, source. Returns: to, from, appUrl, visitors
get_website_cmsGet Website CMSARead-onlyIdempotentInspect
Reads every connected website CMS: provider, site URL (the CMS's own address), the workspace website it publishes to (site: id and domain; one site per workspace publishes for now), status, publish mode (draft or live), each channel's collection and field mapping (including which provider that channel publishes to), the collections and fields Mark discovered (localized: true when the collection stores its title and slug per language, so submit_post needs a website post's language there), locales (the site's language codes, [] when it stores none), and a suggested mapping for each unmapped channel. mediaCollections lists the media collections Mark can read (search_cms_media and save_post take any of them; a website cover takes only the collection its cover field accepts, else mediaDestination); mediaDestination is where Mark uploads a website post's Mark images when it publishes (null: Mark uploads nothing). The API key is never returned. Connecting (site URL and key) happens in the Mark app only. Related: update_website_cms, submit_post, approve_posts.
Inputs: no named parameters. Returns: cms, appUrl
get_weekly_briefGet Weekly BriefARead-onlyIdempotentInspect
The Monday weekly brief members get by email: posts waiting for review (a refresh Mark queued for a live page carries refreshReason, the same reason line the Review tab shows), the posts going out in the 7 days after it is sent (upcoming: approved and scheduled posts, those someone publishes by hand first, each with id, channel, title, scheduledAt and handoff, where a LinkedIn Article reads publish_by_hand; capped at 10, with totalCount and manualCount; the 7 days run from that Monday's 08:00 send time, so a mid-week read still lists posts whose time has passed), the last 7 settled Search Console days against the 7 before with tracked keywords that moved, the priority market's latest AI answer check (named and cited per engine, share of voice, wrong answers flagged that week), and up to 3 suggested next actions. A section with no data is null. weekOf picks the Search days; posts in review, tracked keywords and AI answers are always the current ones. Returns markdown and a fenced JSON block with app links. Does not change anything or send email. Related: get_search_performance, get_ai_visibility, search_posts.
Inputs: weekOf. Returns: search, weekOf, waiting, upcoming, +4 more
get_workspaceGet WorkspaceARead-onlyIdempotentInspect
Shows which Mark workspace you're acting in: your role, timezone, members, sites, integrations, one table row per channel (connected account, approval mode, approver role), and what needs attention now (posts in review, search exceptions, readiness issues). mcpUrl is this workspace's own MCP address, for adding it as a separate connector in clients like Claude that take each URL once. Use it first in a session, or when switching tasks, to see the workspace state. For which social accounts are connected and each channel's rules, call list_channels. For website posts (blog, FAQ, competitor pages), call get_website_cms. Does not change anything. Related: list_channels, get_brand_context, list_connections, search_posts, get_website_cms.
Inputs: no named parameters. Returns: name, role, orgId, sites, +8 more
inspect_urlInspect URLARead-onlyIdempotentInspect
Asks Google whether a page is indexed and why or why not (coverage, canonical, last crawl). Uses your Google quota. Related: get_search_performance, get_site_audit.
Inputs: url (required). Returns: inspection
list_asset_foldersList Asset FoldersARead-onlyIdempotentInspect
Lists the Google Drive folders that hold at least one synced asset, as paths like "Brand Assets/Campaigns/Winter push", sorted. A parent folder holding only subfolders appears only inside its children's paths. Pass a path as search_assets folder to get the assets in it and its subfolders. Lists at most 2,000; capped is true when there are more. Does not change anything. Related: search_assets.
Inputs: no named parameters. Returns: capped, folders
list_channelsList ChannelsARead-onlyIdempotentInspect
Lists every social channel Mark publishes to (LinkedIn, Instagram, Facebook, X, TikTok, Pinterest, YouTube): whether an account is connected and which one, plus the channel's approval mode, approver role, active flag, posting time (24-hour HH:MM in the workspace timezone: when its first post on a slotted day goes out, each further post 3 hours later), and posting slots (each a weekday and a count of posts that day, with its slot id), and appUrl: the channel's page in Mark, where people set its slots and rules. How often a channel posts is its posting slots (update_posting_slots); its posting time is set with update_channel_settings. rulesSaved is false when the channel runs on Mark's default rules because none were saved. This is where to check whether a social account such as a LinkedIn page is connected. list_connections shows integrations instead, where all social accounts share one Social publishing row. Optional timezone (IANA) is checked and otherwise unused: slots have no time of their own. Does not change anything. For which Pinterest board new pins go to, call list_pinterest_boards. Related: get_connect_link, update_channel_settings, update_posting_slots, get_channel_rules, list_pinterest_boards.
Inputs: timezone. Returns: appUrl, channels
list_claimsList Claims and SourcesARead-onlyIdempotentInspect
Lists the Claims & sources page: claims (facts drafts may state, each backed by a quote from a source) and the sources they quote. Each claim has its text, category (the workspace's own short label), sourceQuote, and its source's id, title and url. Active claims are the ones Mark's drafting and post checks use. A retired claim is kept (retiredAt) because posts that cite it keep its wording; it is never active again. Sources carry their type (note is pasted text; drive_doc syncs from Google Drive), how many claims cite them (claims, active and retired; activeClaims), and hasText (a source without text cannot back a quote). query searches claim text, quotes and source titles, and source titles, links and text, like the page's search. Pass sourceId to see one source's full text (body) and only its claims, before quoting it in save_claim. Does not change anything. Related: save_claim, retire_claim, add_source, update_source, delete_source, get_brand_context.
Inputs: query, status, category, sourceId. Returns: appUrl, claims, counts, sources
list_competitorsList CompetitorsARead-onlyIdempotentInspect
Lists the competitors this workspace tracks (up to 50), with ids, names, aliases (other names AI answers count as naming them) and domains. Each one's comparisonPages gives, per kind (vs, pricing), the live post's postId and status, or null when it has none. Pass includeUntracked to also see ones that were unstarred. Related: track_competitor, rename_competitor, set_competitor_aliases, get_competitor_scorecard, draft_competitor_page.
Inputs: includeUntracked. Returns: appUrl, competitors
list_connectionsList ConnectionsARead-onlyIdempotentInspect
Lists integrations (Google, Search Console, GA4, Drive, analytics, CRM, email, Social publishing, and the website CMS) with health, last sync and failed jobs. Social publishing is one integration covering every social account, so this does not show which LinkedIn, Instagram or other accounts are connected: call list_channels for that. On the zernio row, postsLastSyncedAt is when social posts last synced, set only while imports have failed for over an hour and the row stays connected (Mark keeps retrying; the customer has nothing to do); null otherwise. For the website CMS, call get_website_cms. For google_drive, lastSyncSummary has the last finished sync's counts: created, updated, unchanged, removed, failed, complete, and skipped by reason (shortcut, unsupported_type, too_large, trashed). removed counts files that were deleted, trashed or moved out of the shared folders in Drive: they leave the Library and the source list on the next sync, and posts and claims already using them keep them. Nothing is removed when complete is false or failed is above 0. nextAction is the one-line total. What the customer can change per reason: shortcut, move or copy the original files into the shared folder (Mark does not follow shortcuts); unsupported_type, download Sheets, Slides, or Forms as PDF into the folder; too_large, files over 400 MB are not imported; trashed, restore the file. Connecting still happens in the Mark app — call get_connect_link for the in-app URL, then sync_connections. connectionId is the provider name used by sync_connections, update_connection_settings, disconnect_connection and get_connect_link. Related: list_channels, get_connect_link, sync_connections, get_website_cms.
Inputs: no named parameters. Returns: appUrl, connections
list_ideasList IdeasARead-onlyIdempotentInspect
Lists content ideas: things worth making content about, before or alongside the posts made for them. Filter active (default, not archived), no_posts (active, with no linked post) or archived, optionally by pillar. Each idea has its brief, pillar, angle, persona, buyer question, linked posts counted by stage, last published date, and, when archived, covered (has a published post) or dropped. Ideas never close on their own: only save_idea with archived: true closes one. Returns every tab's count. Does not change anything. Related: save_idea, save_post (ideaId).
Inputs: filter, pillarId. Returns: ideas, appUrl, counts
list_market_locationsList Market LocationsARead-onlyIdempotentInspect
Searches the DataForSEO location and language catalog by place name (and optional language name) so you do not have to guess codes. Returns locationCode, place name, country, and languages available there. Does not save a market. Related: set_up_market, save_strategy_item, get_setup_status.
Inputs: place (required), limit, language. Returns: more, locations, nextCursor
list_pinterest_boardsList Pinterest BoardsARead-onlyIdempotentInspect
Lists the connected Pinterest account's boards (id, name, privacy), read live from Pinterest, and currentBoardId: the default board new pins go to when the post names no boardId (null if none is stored). Use it to turn a board the requester names into its id, for a post's boardId or for set_pinterest_board. Fails when Pinterest is not connected or the board list cannot be loaded. Does not change anything. Related: set_pinterest_board, list_channels.
Inputs: no named parameters. Returns: appUrl, boards, currentBoardId
list_saved_keywordsList Saved KeywordsARead-onlyIdempotentInspect
Lists your saved keywords with tags and exclusions. For tracking status and latest rank, call get_rankings. Does not change anything. Related: save_keywords, get_rankings.
Inputs: page, limit, query, cursor, +5 more. Returns: more, page, items, total, +3 more
list_sitesList SitesARead-onlyIdempotentInspect
Lists your websites with the primary site, agent-readiness score, latest audit summary, the lead-capture config save_site writes (capture), snippet (the exact pasteable snippet the Websites page shows, or null), snippetUnavailable (top level: why snippets are withheld, in the Websites page's words, or null once the PostHog reverse proxy is live), snippetNeedsRepaste: true when the snippet on the site's live pages no longer matches its settings, such as different Consent Mode defaults or a Tag Manager container or Cookiebot id that was removed, aiAssistantFile (the exact AI assistant tracking file the Websites page shows for that site, or null), and aiAssistantFileUnavailable (top level: why there is no file, in the Websites page's words, or null when the workspace has its own PostHog project). When snippetNeedsRepaste is true, give the operator that site's snippet to paste in place of the old one on every page; if snippet is null, send them to sitesUrl. aiAssistantFile is a Next.js 16+ proxy.ts that records AI assistant fetches in the workspace's PostHog project; its header comment says where to save it, or how to merge it into an existing proxy.ts. After the operator deploys it, check_ai_assistant_tracking confirms the install. Does not change anything. Related: get_site_audit, check_site_readiness, check_ai_assistant_tracking, save_site.
Inputs: no named parameters. Returns: sites, appUrl, snippetUnavailable, aiAssistantFileUnavailable
list_wrong_answersList Wrong AnswersARead-onlyIdempotentInspect
Lists wrong AI answers, newest seen first: ones members logged from any engine (including ones Mark doesn't check weekly), and competitor_instead ones the weekly check filed (source weekly_check). Each has a reason. Filter by status and buyer question. Does not change anything. Related: save_wrong_answer, get_buyer_question_answers, save_idea (wrongAnswerId).
Inputs: limit, status, question. Returns: appUrl, wrongAnswers
record_post_publishedRecord Post PublishedAIdempotentInspect
Records that an approved post Mark doesn't publish itself is live: a LinkedIn Article (channel linkedin_article), or a website post (blog, faq, competitor_page) whose post type no CMS connection maps (get_post handoff reads publish_by_hand). Pass its id and its live URL: for an Article, the https linkedin.com URL LinkedIn shows for it; for a website post, the page's https address on any host. The post becomes published with publishedAt now and externalUrl the URL, the same as a post Mark published, and Mark sends post_published to the workspace's PostHog project once, on the first recording. For an Article, Mark reads its LinkedIn metrics daily for 30 days, then weekly until 90 days, through the connected LinkedIn account. Mark never publishes an Article itself, since LinkedIn has no API for them; a person copies the approved Article into LinkedIn at its slot time (get_post handoff reads publish_by_hand, then publish_now), then this records it. A website post with no CMS mapping is copied onto the site by hand (title, Markdown body, slug, SEO title and description from get_post), then recorded here. Calling again with the same URL succeeds and changes nothing. Refused for a post that isn't approved, another channel, a website post whose post type a CMS maps (Mark publishes those through the CMS), a URL that isn't https, an Article URL that isn't on linkedin.com or has no urn:li:share: or urn:li:ugcPost: post URN (a /pulse/ address), or a published post with a different URL. To help the person copy an Article, get_post gives the headline (meta.title) and the Markdown body; the cover is the asset meta.cover.assetId names, whose URL get_asset returns. Only use it once the user says the post is live. Related: get_post, get_asset, approve_posts, get_calendar.
Inputs: url (required), postId (required). Returns: appUrl, postId, status, externalUrl, +1 more
refresh_ai_volumeRefresh AI Search VolumeAInspect
Re-estimates how often your buyer questions are asked in AI assistants. Fetches a fresh estimate from the provider for each active buyer question in every active market with a location and language; get_ai_visibility then shows each question's total across those markets and the per-market split. scope.summary states it as questions × markets. If this refuses because the workspace is not set up, call get_setup_status and check its rows, including skipped ones. Related: get_ai_visibility, save_strategy_item, set_up_market.
Inputs: no named parameters. Returns: count, scope, state, markets
remove_competitor_lookupRemove Competitor LookupADestructiveInspect
Removes an untracked domain from Other lookups on the competitor scorecard, in every market: deletes its unstarred competitor record and every stored lookup of it. Looking it up again runs a new lookup, and tracking it again starts fresh. Refuses a tracked competitor (untrack it first) and your own sites. A domain with nothing stored removes nothing. Related: list_competitors, untrack_competitor.
Inputs: domain (required). Returns: domain, removedLookups, removedCompetitor
remove_keywordsRemove KeywordsADestructiveInspect
Removes saved keywords and their tracking history from the workspace. This cannot be undone. Related: list_saved_keywords, save_keywords.
Inputs: savedKeywordIds (required). Returns: removed
rename_competitorRename CompetitorAIdempotentInspect
Changes a competitor's name, tracked or not, like the rename on its profile. AI answer matching uses the new name for past and future weekly answers. Trims the name and refuses a blank one or one over 100 characters. Related: list_competitors, track_competitor, set_competitor_aliases.
Inputs: name (required), competitorId (required). Returns: competitor
report_feedbackReport FeedbackAInspect
Records what the person asked you to change while drafting, and your own checks on the draft, so Mark can later suggest brand rules. Each call stores one new report. Call it at two moments: when the person states a standing rule ("never say game-changer"), before you ask whether to save it; and once on the version they accept, after save_post, with that postId. For a piece that lives outside Mark, pass externalRef instead. Each iteration is one ask: a short paraphrase in ask, its category, and scope (tenant_wide when they said it applies to everything, this_piece when only here, unsure otherwise; never guess). before, after and userQuote are optional short excerpts of the changed words and of what the person said. Keep every excerpt short, and never paste the conversation or a full draft. selfChecks list the checks you ran (check, result pass/warn/fail, reason). Recording feedback changes no rule. When the person agrees a rule, save it with save_content_exclusion (pass this report's id as feedbackId) for a phrase Mark should block or warn on, or update_brand_profile for voice guidance. Asks scoped tenant_wide or unsure come back as suggested rules from suggest_brand_rules. Related: save_content_exclusion, update_brand_profile, save_post, suggest_brand_rules.
Inputs: channel (required), notes, postId, iterations, +2 more. Returns: id
reschedule_postReschedule PostAInspect
Moves a post to a new future time. A draft keeps its status; any other post goes back to review at the new time and publishes only once approved again. Pass scheduledAt for any time, or slotId and date (both, from get_calendar's open slots) to fill one posting slot occurrence: a slot is a day and a number of posts, so Mark puts the post in that day's next free position, the channel's posting time plus 3 hours per post already there, or the next minute once that has passed today, in the workspace timezone. It refuses a slot on another channel, a day before today, or a full day, so two callers can't overfill it. The same status rules apply either way. Related: get_post, get_calendar, search_posts.
Inputs: postId (required), date, slotId, scheduledAt. Returns: rev, appUrl, postId, status, +1 more
research_keywordsResearch KeywordsAInspect
Finds keyword ideas for one to five seed terms. On labs, each idea contains its seed term. Every idea carries seed, the seed term it came from (null for a multi-seed google_ads run). Each call makes 1 request per seed term to the keyword provider (google_ads: 1 request in total), returning up to limit ideas in total. Each call spends provider calls. Every idea has search volume, CPC and competition. labs (the default) adds difficulty and intent; google_ads, for countries labs does not cover, has neither. A SERP overview per idea comes only with includeSerpInfo, on labs. With google_ads, close variants come back as one row listing them in closeVariants; its volume is the group's, so count it once. google_ads allows about 12 calls a minute across Mark: on a RATE_LIMITED error, wait the seconds it states or run calls one at a time. The text table lists up to 100 ideas; every idea is in the structured result. Results are not saved until you call save_keywords. If this refuses because the workspace is not set up, call get_setup_status and check its rows, including skipped ones. Related: check_keyword_audience, save_keywords, set_up_market.
Inputs: seedKeywords (required), limit, source, languageCode, +2 more. Returns: items, taskId
resolve_cms_conflictResolve CMS ConflictADestructiveInspect
Settles a website CMS conflict on a blog, FAQ or competitor post, the same choice as the review page buttons. Read get_post first: website.state is conflict and website.conflictDiff lists each changed field with its CMS value (cms) and Mark's value (mark). Show the user that diff and only call this when they have chosen. overwrite replaces the CMS page with Mark's version and discards the edits made in the CMS. On an approved or scheduled post Mark sends it now (outcome overwrite_queued); on a post not yet approved Mark overwrites on the next send_cms_preview or approval (overwrite_on_next_send); on a post whose last send failed, retrying that send overwrites (overwrite_on_retry). keep leaves the CMS page as it is (outcome kept). The post is then out of sync (website.state out_of_sync) and its review note says Mark kept the CMS version. Refused when the post is not waiting on a CMS conflict choice. Related: get_post, send_cms_preview, approve_posts.
Inputs: choice (required), postId (required). Returns: outcome
resolve_external_postResolve External PostAInspect
Keeps a post published outside Mark as an unmatched external post, or links it to an existing Mark post. Admin only. Related: list_connections, get_post.
Inputs: action (required), externalPostId (required), targetPostId. Returns: appUrl, outcome
retire_claimRetire ClaimADestructiveInspect
Retires an active claim on the Claims & sources page. Drafts and post checks stop using it. It stays listed as retired (list_claims status retired), because posts that cite it keep its wording, and it cannot be reactivated: to state the fact again, add a new claim with save_claim. To change a claim's wording, call save_claim with its id instead; that retires it and creates the new one. Related: list_claims, save_claim, get_brand_context.
Inputs: id (required). Returns: claim, appUrl
review_postRequest Changes or RejectADestructiveInspect
Sends a post back with decision: changes_requested plus a note, or rejects it (rejected, final). Needs the current rev. To approve, use approve_posts. On a channel set to admins only, only workspace admins can decide; anyone else is refused. A rejected post will not publish. Requesting changes unlocks the draft for editing. Rejecting a website post that sent a preview to its CMS deletes that preview draft when Mark may; otherwise cmsPreview says it is still in the CMS, where, and why, and so does the post's review note. Related: get_post, approve_posts, comment_on_post.
Inputs: rev (required), note (required), postId (required), decision (required). Returns: appUrl, postId, decision, cmsPreview
run_answer_engine_checkRun AI Answer CheckAInspect
Starts this week's AI answer check now, the same weekly run the schedule would start, and reports where it stands. There is at most one run per week, so calling it again returns that run with its progress (completed, failed and expected counts) and does not start a second. A workspace exempt from the weekly limit gets a new run instead once this week's run has finished; the earlier run and its answers are kept, and AI answers keeps showing them until the new run finishes. Also says when the next run is due, or why nothing was started: AI answer checks are off (an admin turns them on in Settings > Workspace; this tool does not), no buyer questions, or no market location. Covers every active market with a location; it takes no market. One run asks every active question in every such market on 6 engines (ChatGPT, Gemini, AI Overview, AI Mode, Claude and Perplexity; Claude and Perplexity through their APIs with web search on, not the consumer apps); scope.summary states it as questions × engines = checks (× markets when more than one), and get_ai_visibility's nextAnswerCheck shows it before you start. When this week's run exists, scope is that run's. Limited to 10 calls a minute per workspace when rate limiting is configured; otherwise calls pass, and the one-run-a-week rule still holds for every other workspace. Related: get_ai_visibility, set_up_market.
Inputs: no named parameters. Returns: run, scope, state, appUrl, +1 more
run_rank_checkRun Rank CheckAInspect
Starts an on-demand rank refresh for tracked keywords, outside the weekly schedule. Checks the Google top 100 for each tracked keyword, 1 check per keyword in the tracker's location and language (scope.summary, like 12 keywords = 12 checks); results can take 20 minutes or more. Pass trackerId from get_rankings, or siteId to use that site's tracker. Related: get_rankings, save_keywords.
Inputs: siteId, trackerId. Returns: run, scope, state
run_site_auditRun Site AuditAInspect
Starts a new crawl and audit of a site, up to 50 pages. Results arrive in minutes; poll get_site_audit. If this refuses because the workspace is not set up, call get_setup_status and check its rows, including skipped ones. Related: get_site_audit, list_sites, save_site.
Inputs: siteId (required). Returns: audit, scope, state
save_claimSave ClaimAInspect
Adds or revises a claim on the Claims & sources page. A claim is one fact drafts may state (a price, a result, a certification, a feature), backed by a quote that must appear in its source's text. Claims are append-only, so posts that cite a claim keep the wording they were approved against:
Add: text, category, sourceQuote, and either sourceId (list_claims or get_brand_context sources) or source {title, body, url?} to create the source too.
Revise: id of an active claim plus only the fields that change. This retires that claim and creates a new one; the result has the new claim and retiredId. Each call creates a claim. To stop drafts using a claim, call retire_claim. Only active claims reach Mark's drafting and post checks. Related: list_claims, retire_claim, add_source, get_brand_context.
Inputs: id, text, source, category, +2 more. Returns: mode, claim, appUrl, retiredId
save_content_exclusionSave Content ExclusionAIdempotentInspect
Creates or retires a content exclusion: a literal phrase Mark enforces (block or warn), optionally limited to one channel. BrandProfile (voice.do, voice.dont, voice.principles, doctrine) is prose guidance the model reads. ContentExclusion is phrases Mark enforces. To change an exclusion, retire it and create a new one — exclusions are never edited in place. Pass phrase and severity to create, plus feedbackId when report_feedback recorded the person asking for this rule. Pass id and active: false to retire. Related: get_brand_context, update_brand_profile, report_feedback.
Inputs: id, active, phrase, reason, +3 more. Returns: exclusion
save_ideaSave IdeaAInspect
Creates, edits, archives or unarchives a content idea. To create: pass title; brief, angleId or pillarId, and buyerQuestionId are optional. To edit: pass id and only the fields that change. To archive or unarchive: pass id and archived: true or false. Archiving never changes the idea's posts. To tie existing posts to an idea: pass id and linkPostIds (from search_posts). It changes only each post's idea, in any status: a post in review, scheduled or published keeps its rev and approval, and a post on another idea moves here. The idea must be active. unlinkPostIds unlinks posts from this idea. One call applies its changes in order (fields, unarchive, unlink, link, archive); a refusal leaves the earlier ones saved, so call list_ideas to check before retrying. To log an idea from a wrong answer: pass wrongAnswerId (from list_wrong_answers); title, brief and buyer question come from it. An angle sets the idea's pillar and persona to the angle's; a pillarId that contradicts the angle is refused. For a pillar on its own, pass angleId null. To draft a post from an idea, call save_post with channel and ideaId. Related: list_ideas, save_post, search_posts, get_brand_context.
Inputs: id, brief, title, angleId, +6 more. Returns: idea, appUrl
save_keywordsSave KeywordsAIdempotentInspect
Saves keywords (from research or Search Console) and sets their tags, exclusion and rank tracking, in one call. New keywords are tracked and audience-checked (one Google top 10 fetch each) unless track is false or they are excluded. Exclude a keyword whose searchers aren't the buyer; it stops tracking. If this refuses because the workspace is not set up, call get_setup_status and check its rows, including skipped ones. Related: research_keywords, list_saved_keywords, set_up_market.
Inputs: items (required). Returns: saved, outcomes
save_postSave PostAInspect
Creates a new draft (omit postId; channel required) or edits an existing post: text, channel fields, media, angle, pillar, buyer questions, proposed time. Never submits or publishes. Returns a save receipt: scheduledAt, assetIds, channel, counted body length, buyerQuestions, idea, language and translationOf as stored, updatedAt, and checks (Mark's content checks on the saved content, including placeholder, and keyword on a website post with meta.seo.keyword; a fail is not a refused save, but submit_post refuses a draft while it fails). On a draft or changes-requested post, rev does not change; use updatedAt to confirm the save. A post in review, approved or scheduled needs rev (from get_post): the edit saves as the next rev and the post goes back to in review, so a person approves it again; a stale rev is refused, and so is a phrase this workspace blocks, a placeholder like [needs source], a new website slug another page in your CMS uses, or no language on a website post whose CMS collection stores pages per language. On those posts ideaId or guideVersion alone is refused, since it would drop the approval without changing what publishes. To link any post to an idea without touching its approval, call save_idea with linkPostIds. Posts in any other status are locked; to fix the text of a published social post, call edit_published_post. Any post that is not archived, published and imported posts included, takes postId with angleId and/or pillarId alone (rev optional): that changes only its pillar, angle and persona, so its status, rev, content and approval stay as they are. A website post that is not archived (in review, approved, scheduled, published, failed or missed) also takes postId with buyerQuestionIds alone: that replaces the buyer questions it answers and nothing else, so its status, rev, content and approval stay as they are. Asset ids that are not in this workspace are listed as dropped, never attached silently. Likewise droppedMetaKeys lists the meta keys the channel does not take, at any depth, as paths like cover.url or cmsImages[0].alt, which the save ignored; the save still succeeds. A LinkedIn post can carry one PDF asset (kind document) and no other media, up to 100 MB; meta.documentTitle is the title LinkedIn shows above it, else the file name. A PDF with other media, over 100 MB, or on another channel is refused with the message submit_post gives. An angle sets the post's pillar and persona to the angle's; a pillarId that contradicts the angle is refused. Website posts (blog, faq, competitor_page) take a pillar, never an angle, plus the buyer questions the page answers (buyerQuestionIds, from get_ai_visibility). A LinkedIn Article (linkedin_article) takes a Markdown body and meta { title, cover? }; it needs no connected account, and once approved it waits for a person to publish it on LinkedIn and record the URL with record_post_published. ideaId links the post to an idea from list_ideas (null unlinks); linking never changes the post's pillar, persona or angle. A new draft with ideaId takes what the call leaves unset from the idea: its pillar and angle (a website post takes the pillar and persona, never the angle), its buyer question on a website post while that question is active, and a body that starts with the idea's title and brief. Call get_channel_rules first. To send it for review, call submit_post after saving. Related: get_post, submit_post, add_asset, list_ideas.
Inputs: rev, body, meta, ideaId, +10 more. Returns: rev, idea, appUrl, assets, +17 more
save_siteSave SiteAIdempotentInspect
Adds or edits a website: hostname, name, whether it is primary, and lead-capture settings for the site tag. Pass siteId to edit. Changing a site's hostname clears its AI readiness score and site audits. Related: list_sites, delete_site.
Inputs: name, domain, siteId, capture, +1 more. Returns: appUrl, siteId
save_strategy_itemSave Strategy ItemAIdempotentInspect
Creates or updates one strategy item: kind = persona | pillar | angle | market | buyer_question | kpi_target. Personas, pillars and angles are the Messaging page (/messaging); an angle is one persona × pillar pair, so create it with fields.personaId and fields.pillarId from get_brand_context. Markets are the Markets page (/markets); to add one from a place name, set_up_market resolves the codes for you. Pass id to update an existing item. For a persona, pillar, angle or market, send only the fields that change; the rest, and the active flag, keep their saved values. Pillar and angle slugs cannot change after creation. A new market without priority goes after every existing one; a locationCode and languageCode pair must be one list_market_locations returns. Pass id and active only (no fields) to deactivate (false) or reactivate (true) a persona, pillar, angle, market, or buyer question without deleting it. KPI targets are the Targets page (/targets): one target per metric per month. Saving a metric and month that already has one is refused with that target's id; to change it, pass its id and fields.value only (metric and month cannot change). engagement_rate is a percent: send 4.5 for 4.5%. KPI targets have no active flag — passing active is refused. Delete a KPI with delete_strategy_item. Read targets with their actuals and progress from get_content_performance (kpis). Buyer question text cannot be edited in place — create a new one, retire it with active: false, or delete it with delete_strategy_item. A buyer question's topic (a short label, max 60 characters) can change any time: pass id with fields.topic, or empty to clear. It touches no past or future check. Creating a buyer question also returns item.firstCheck { questions, markets, engines, checks }: what the next AI answer check sends (active questions × active markets with a location × engines). suggest_buyer_questions lists questions to start with. Returns appUrl, the page where a person sees the item. Related: get_brand_context, suggest_buyer_questions, delete_strategy_item, set_up_market, list_market_locations, get_content_performance.
Inputs: kind (required), id, active, fields. Returns: item, kind, appUrl
save_wrong_answerSave Wrong AnswerAInspect
Logs a wrong AI answer, or resolves or reopens one. To log: pass engine, reason, prompt, quote and seenOn; question, market and note are optional. To resolve or reopen: pass id and resolved: true or false. Logged rows are never edited. Related: list_wrong_answers, get_buyer_question_answers, save_idea (wrongAnswerId).
Inputs: id, note, quote, engine, +6 more. Returns: appUrl, wrongAnswer
search_assetsSearch AssetsARead-onlyIdempotentInspect
Searches the media library (images, video, documents) by text, type, source or Google Drive folder, newest first without text. Reaches the first 50 matches across all pages; capped is true when a page reached that limit and more may exist, so narrow with q, kind, source or folder. Does not change anything. Related: get_asset, add_asset, list_asset_folders.
Inputs: q, kind, limit, cursor, +2 more. Returns: more, items, capped, nextCursor
search_cms_mediaSearch CMS MediaARead-onlyIdempotentInspect
Searches the images already in one of the connected CMS's media collections, newest first, 24 per page. get_website_cms lists the collections Mark can read (mediaCollections); left out, Mark uses the default, and the reply names the collection it searched. Use a result as a website post's cover with save_post meta.cover = { source: "cms", mediaId, collection }, or attach it to a social post with save_post meta.cmsImages = [{ mediaId, collection }]: Mark keeps a reference, never a copy, and a social post's image is copied to the platform when the post is scheduled. To put one in a website post's body, add on its own line with the result's url (the original file): on a rich-text (lexical) mapping Mark publishes it as that media document, uploading nothing.
Does not change anything; reads from the customer's CMS. These ids are not Mark asset ids. Related: save_post, get_website_cms, search_assets.
Inputs: page, query, channel, collection. Returns: items, collection, hasNextPage
search_postsSearch PostsARead-onlyIdempotentInspect
Finds posts by status (in_review is the review queue), channel, scheduled time range, angle, pillar, text, or website CMS delivery state. Returns each post's current rev, updatedAt, mediaCount (assets, a website cover and images from the CMS; 0 means no media, so finding posts without an image needs no get_post per post) and, for website posts, the buyer questions it answers (buyerQuestions). A refresh of a live website page Mark published carries refresh: { text, pageUrl }, the reason Mark queued it for review; refresh is null on every other post. An approved social post moves to scheduled within seconds while Mark hands it to the publisher; handoff says handing_off then, or stuck with the reason and fix when it can't go (no scheduled time, no connected account, or no progress 5 minutes after approval). An approved LinkedIn Article carries handoff publish_by_hand before its scheduled time and publish_now from it on, each with the reason (a person publishes it on LinkedIn). handoff is null on every other post. status: approved still matches handing-off, stuck, publish_by_hand and publish_now posts. rev is the frozen revision: it stays 0 on drafts and only advances when the post is submitted (or frozen). updatedAt confirms a draft save. from and to filter by scheduled time, not created or updated. Each accepts YYYY-MM-DD or a full ISO timestamp. A date-only to includes that whole day in the workspace timezone. Unscheduled drafts never match a date range. websiteState filters blog, FAQ and competitor posts by CMS delivery: preview (a pre-approval preview draft in the CMS, not live), queued, sent_to_cms (saved in the CMS, not live), published, taken_down, failed, removed, conflict, out_of_sync, or none: the same values as get_post's website.state. Optional timezone (IANA) renders scheduled and published times in that zone; it does not change stored data. Does not change anything. Related: get_post, approve_posts, review_post.
Inputs: to, from, text, limit, +7 more. Returns: more, items, nextCursor
send_cms_previewSend CMS PreviewAInspect
Saves a blog, FAQ or competitor post that is not yet approved (draft, changes requested, or in review) to its website CMS as a draft, so a reviewer can open it on the customer's own site before approving. Sends the post's current saved content. Always a draft, even when the connection publishes live, and nothing goes live. Sending again updates the same CMS document; approving later updates it too. Refused, with the reason, for an approved, scheduled or published post, a channel with no CMS mapping, or a collection without drafts. Refused as a conflict when the CMS page changed since Mark last sent it; choose overwrite or keep with resolve_cms_conflict. Refused when another page in the CMS uses the post's slug: the error names each page (CMS id, title, its latest draft when it differs, live or draft, edit link, and the Mark post that owns it, if any) and says which ids useCmsPageId accepts. Then either change the slug with save_post, or call again with useCmsPageId to send into that page. Does not approve or change the post's status. Related: get_post (website.previewUrl, previewSentAt), save_post, approve_posts.
Inputs: postId (required), useCmsPageId. Returns: state, sentAt, previewUrl
send_setup_checklistEmail Setup ChecklistAInspect
Emails the signed-in user the setup checklist: the rows still to do (the same rows get_setup_status and Home list) with a link to each, the rows skipped and done, and every connection waiting on the customer with its next step. Sends nothing when no row is to do and no connection is waiting. Related: get_setup_status, list_connections.
Inputs: no named parameters. Returns: sent, message
set_business_aliasesSet Business AliasesAIdempotentInspect
Replaces your business's other names (up to 5). An AI answer that uses your business name or any alias counts as naming you, for named rate and share of voice, in past weeks' answers too. Trims each name, drops duplicates and the business name itself; an empty list goes back to the name only. Any member can call it. Related: get_workspace, get_ai_visibility, set_competitor_aliases.
Inputs: aliases (required). Returns: businessName, businessAliases
set_check_cadenceSet Check CadenceAIdempotentInspect
Sets how often a rank check or site audit runs: cadence off (manual only) or weekly. Saving a cadence never starts a check. For rank, pass trackerId from get_rankings to set one market, or siteId to set every market the site tracks (neither means the primary site); nextAt is the earliest next check. For site_audit, pass siteId. Related: run_rank_check, run_site_audit.
Inputs: check (required), cadence (required), siteId, trackerId. Returns: check, nextAt, cadence
set_competitor_aliasesSet Competitor AliasesAIdempotentInspect
Replaces a competitor's other names (up to 5). An AI answer that uses its name or any alias counts as naming it, in past weeks' answers too. Trims each name, drops duplicates and the competitor's own name; an empty list goes back to the name only. Related: list_competitors, rename_competitor, get_competitor_scorecard.
Inputs: aliases (required), competitorId (required). Returns: competitor
set_pinterest_boardSet Pinterest BoardAIdempotentInspect
Sets the default board new pins go to when a post names no boardId. boardId must be on the connected account's board list (list_pinterest_boards). Posts that name their own boardId, and pins already scheduled or published, keep their board. Call it only when the requester asks to change the default board, not to place one pin: for that, set boardId on the post. Related: list_pinterest_boards, list_channels.
Inputs: boardId (required). Returns: appUrl, boardId, boardName
set_up_marketSet Up MarketAIdempotentInspect
Creates or updates an active market from a place name and language, resolving DataForSEO location and language codes. If the place or language is ambiguous, it refuses and returns candidates instead of guessing. A market already stored for that location is updated and reactivated, keeping its name, priority and time zone unless you pass them. Does not start a rank check. To rename, deactivate or delete a market, use save_strategy_item or delete_strategy_item (kind: market) with marketId. Related: list_market_locations, get_setup_status, get_brand_context, research_keywords.
Inputs: place (required), language, priority, timezone. Returns: name, active, appUrl, outcome, +6 more
set_workspace_timezoneSet Workspace TimezoneAIdempotentInspect
Sets the workspace IANA timezone. Only org admins can call it. Each channel keeps its posting time as a local clock time, so real posting instants move. Already-scheduled posts keep their times. Related: get_workspace, get_calendar, update_posting_slots.
Inputs: timezone (required). Returns: newTimezone, oldTimezone, affectedSlotCount
show_postShow PostARead-onlyIdempotentInspect
Use this when the person wants to see or review one post themselves: "show me the pricing post", "let's review this one". In Claude and ChatGPT it draws the post's preview with Approve, Request changes and Reject when it is in review, and its comments with a box to add one (comment_on_post); other clients get the same text as get_post. One post per call, and only for what the person asked to see. To read posts for a task (editing, checking media, approving, going through several), use get_post, which draws nothing. Related: get_post, search_posts, review_post.
Inputs: postId (required), timezone. Returns: post
skip_setup_stepSkip Setup StepAIdempotentInspect
Skips one setup checklist row, or with skipped false brings it back, for everyone in the workspace, the same as Skip and Undo skip on Home's setup card. Use it when the user says they don't need a step. A done row stays done and is left as it is. Returns the updated rows, as get_setup_status does. step is one of: website, market, brand_voice, web_analytics, content_sources, social, buyer_questions, posting_times, connect_agent. Related: get_setup_status.
Inputs: step (required), skipped. Returns: gaps, rows, ready, appUrl
submit_postSubmit Post for ReviewAInspect
Sends a draft to review. If the channel is set to auto-approve and there are no claim flags, Mark approves it. The result says which happened and, for website channels, names the next CMS step (queued at a time, or a CMS draft that does not go live); an auto-approved LinkedIn Article stays approved, since a person publishes it on LinkedIn. Refuses a post the channel would reject at publish (no media on Instagram, TikTok, Pinterest or YouTube; more than 10 Instagram carousel items; a PDF on a channel other than LinkedIn, with other media, or over 100 MB; no Pinterest board; over the length limit; missing required meta, including the language of a website post whose CMS collection stores pages per language) and a post that uses a phrase this workspace blocks (the error names each phrase and why) or still has a placeholder such as [needs source] or TODO (the error names each one), and lists what to fix. Content-check warnings never stop it; Mark stores every result for the new revision. After it returns, Mark also checks the revision against the confirmed brand voice and offer and stores a brand_fit result (warn or pass, never blocking) that get_post's revisionChecks shows once it lands. Does not publish immediately unless the post is already scheduled and auto-approved. If this refuses because the workspace is not set up, call get_setup_status and check its rows, including skipped ones. Related: save_post, get_post, approve_posts, get_connect_link.
Inputs: postId (required). Returns: rev, appUrl, postId, status
submit_urls_to_indexnowSubmit URLs to IndexNowADestructiveInspect
Notifies IndexNow search engines of URLs on one of your sites. Verifies the key file first. This cannot be undone: the URLs are sent to IndexNow immediately. Related: list_sites.
Inputs: urls (required), siteId (required). Returns: ok, message, urlCount
suggest_brand_rulesSuggest Brand RulesARead-onlyIdempotentInspect
Lists brand rules suggested from drafting feedback (report_feedback): asks people made while drafting that may apply to everything, with the same ask from several reports listed once. This tool saves nothing. Each suggestion has ask, category, kind (exclusion: a phrase Mark could block or warn on; voice: a voice do or dont line), scopes (tenant_wide, unsure), count (reports that raised it), feedbackIds, and the newest report's channel, createdAt and before, after and userQuote excerpts. Asks scoped this_piece are never listed, and neither are asks from a report that already has a linked exclusion. Unsure asks come first, then higher count, then newest; at most 25, read from the newest 200 reports. Show each suggestion to the person and ask before saving it. On a yes, save an exclusion with save_content_exclusion (pass one of its feedbackIds as feedbackId), or a voice line with update_brand_profile (section voice, the full merged list, without confirm; the person confirms the section on the Brand page at appUrl). On a no, save nothing. A declined suggestion, or one saved as a voice line, can be listed again. state: ready, or none when nothing is waiting. Returns appUrl, the Brand page. Related: report_feedback, save_content_exclusion, update_brand_profile, get_brand_context.
Inputs: no named parameters. Returns: state, appUrl, suggestions
suggest_buyer_questionsSuggest Buyer QuestionsARead-onlyIdempotentInspect
Suggests up to 10 buyer questions to start tracking in AI answers, the same list the empty AI answers tab shows: up to 3 People Also Ask and Search Console questions the workspace already has, then questions written by one AI model call from the Brand page (positioning, products, personas, pillars) and tracked competitors. Each has a source: brand, competitor (with the competitor's name), people_also_ask or search_console. None repeats a question on the list, active or retired. Nothing is saved: add the ones you pick with save_strategy_item (kind buyer_question, fields.question). Each call writes a new list. state: ready; failed (the model call failed, rows from search still listed); intelligence_off (AI answer checks are off, so no model call, rows from search still listed); no_context (nothing to suggest from). Limited to 10 calls a minute per workspace when rate limiting is configured. Related: save_strategy_item, get_ai_visibility, run_answer_engine_check, track_competitor.
Inputs: no named parameters. Returns: state, appUrl, suggestions
suggest_competitorsSuggest CompetitorsARead-onlyIdempotentInspect
The sites that rank for the most of the same Google keywords as one of your sites in one market: up to 20, most shared keywords first, each with shared keywords, estimated monthly traffic and whether you already track it. Leaves out your own sites, search engines, marketplaces, social, review and directory sites, and .gov/.edu sites. Returns the stored list when there is one; with refresh: true, or when nothing is stored, it fetches fresh data (1 domain) and replaces the stored list. Related: track_competitor, list_competitors.
Inputs: siteId, refresh, languageCode, locationCode. Returns: appUrl, fetched, suggestions
sync_connectionsSync ConnectionsAInspect
Refreshes integration health. Pass connectionId (the provider from list_connections) to check one integration, retryJobId to retry a failed sync job, both, or neither to refresh all. It works per integration: zernio refreshes Social publishing as a whole, not one social account. Drive also starts an ingest when a folder is configured. When a website post's approval failed because another CMS page uses its slug (get_post reviewNote lists each page's CMS id), pass useCmsPageId with retryJobId to make that page the post's own and retry in one step. Only a page that is not live and no other Mark post owns is accepted; anything else is refused with the reason and nothing is written. In live publish mode the page is published; in draft mode it stays a draft. Related: list_connections, list_channels, get_post.
Inputs: retryJobId, connectionId, useCmsPageId. Returns: retry, ingest, refresh
track_competitorTrack CompetitorAIdempotentInspect
Starts tracking a competitor's domain, so it shows on the competitor scorecard and in AI answer matches. Tracking the same domain again reuses it and keeps its stored name: name applies only when a domain is first tracked, so rename with rename_competitor. Refuses your own sites, and more than 50 tracked competitors. Related: list_competitors, rename_competitor, untrack_competitor.
Inputs: domain (required), name. Returns: competitor
unpublish_postUnpublish PostADestructiveInspect
Takes a published blog, FAQ or competitor page down from the website CMS and keeps the post in Mark. Workspace admins only. Mark unpublishes the page, reads it back, and only then records taken_down. The CMS document stays, so approving the post again updates the same page. Pass an optional reason stored on the post. Refused when the page is not live, the collection has no drafts, or the unpublish cannot be confirmed. Never returns API keys. Related: get_post, delete_post, approve_posts.
Inputs: postId (required), reason. Returns: state, postId
untrack_competitorUntrack CompetitorAIdempotentInspect
Stops tracking a competitor. Its history stays, and tracking the domain again brings it back. Related: list_competitors, track_competitor.
Inputs: competitorId (required). Returns: competitor
update_assetUpdate AssetAIdempotentInspect
Renames an asset (and later other metadata). Drive-synced files cannot be renamed here. Related: get_asset, search_assets.
Inputs: name (required), assetId (required). Returns: asset
update_brand_profileUpdate Brand ProfileAIdempotentInspect
Edits one section of the Brand page: section=voice (Brand and voice), visual (Visual identity: logo, colors, fonts), overlay (Entities and evidence: primary and secondary entities, experts, evidence, sameAs, Wikidata ids) or offer (Offer: what is sold today, available now, planned and not available yet, pricing, promises, claims to avoid). Pass the section's object with only the fields that change, in the shape get_brand_context returns under profile. Fields you leave out keep their saved values; lists you send replace the saved list; an empty string clears a text field. voice.voice and voice.companyFacts merge key by key, and visual.colors merges by role. Set the logo with visual.logoAssetId, an image id from search_assets or add_asset. Overlay evidence cites a source by sourceId (get_brand_context sources) or a sourceUrl. Confirmed means a person reviewed the section, and Mark's own drafting uses a section only once it is confirmed. Saving clears the section's confirmation unless you pass confirm: true. Only pass confirm: true when the person you act for has reviewed the content and explicitly asked you to confirm it. confirm: true with no fields confirms the section as it is. Otherwise send them the returned appUrl to review and confirm it there. BrandProfile voice (do, dont, principles, doctrine) is prose guidance the model reads. A voice.voice.tone entry can name its limit as ', not ' ('warm, not gushing'). voice.voice.antiExamples holds posts that don't sound like the brand; drafts read them as what to avoid. Claims the brand must not make go in offer.claimsToAvoid, not voice.dont. Literal phrases to block or warn on belong in save_content_exclusion. Related: get_brand_context, search_assets, add_asset, save_content_exclusion.
Inputs: section (required), offer, voice, visual, +2 more. Returns: title, appUrl, values, section, +1 more
update_channel_settingsUpdate Channel SettingsADestructiveInspect
Updates a channel's settings, including approval mode and posting time. postingTime (24-hour HH:MM, workspace timezone, default 09:00) is when the channel's first post on a slotted day goes out; each further post that day goes 3 hours later. Which days it posts, and how many posts each day, are its posting slots: use update_posting_slots. approval=required keeps human review; approval=auto skips human review and can publish as soon as a post is submitted — only use auto when the workspace has chosen to skip review for that channel. approverRole=approver means admins only: only workspace admins can approve, request changes on, or reject that channel's posts, and only admins can change approverRole. Related: list_channels, get_channel_rules, submit_post.
Inputs: channel (required), notes, active, icpFit, +4 more. Returns: appUrl, channel, approval, postingTime
update_connection_settingsUpdate Connection SettingsAIdempotentInspect
Saves a Google Search Console or GA4 property, or Drive folders to watch. Removing a Drive folder takes its files out of the Library on the next sync; removing the last one takes every Drive file out at once. Posts, the brand logo and claims that use them keep them, and adding the folder back returns them. Pass connectionId from list_connections (search_console, ga4, google, or google_drive). Related: list_connections, sync_connections.
Inputs: connectionId (required), product, property, addFolders, +1 more. Returns: added, appUrl, connectionId
update_posting_slotsUpdate Posting SlotsAIdempotentInspect
Adds or removes a channel's posting slots. A slot is a weekday, 1 (Monday) through 7 (Sunday), and a count: how many posts that channel makes that day, 1 to 5 (default 1). A slot has no time: the day's first post goes at the channel's posting time (set with update_channel_settings postingTime, default 09:00 in the workspace timezone), each further post 3 hours later, and a post placed after its time goes out at the next minute. Adding a weekday the channel already has sets that slot's count. Related: list_channels, get_calendar, update_channel_settings.
Inputs: channel (required), add, remove. Returns: added, slots, appUrl, errors, +2 more
update_sourceUpdate SourceAIdempotentInspect
Edits a pasted note (a source with type note) on the Claims & sources page: its title, text (body) or link. Pass id and only the fields that change. Claims quote a note's text, so a body edit is refused when a claim citing the note, active or retired, quotes text the new body no longer contains. The error names those quotes; revise those claims with save_claim first, or keep the text. Line breaks, case and curly quotes are ignored, as in save_claim. Other source types are refused: a drive_doc syncs from Google Drive and is edited there. Related: list_claims, delete_source, save_claim.
Inputs: id (required), url, body, title. Returns: appUrl, source
update_website_cmsUpdate Website CMSAIdempotentInspect
Changes how approved website posts reach a website CMS: pass provider (from get_website_cms) when more than one CMS is connected, then per channel (blog, faq, competitor_page) which collection and fields they fill, mediaDestination (where new media goes), or a switch back to draft mode. Pass rediscover to read the CMS's collections again first. A mapping is checked against the discovered fields before it is saved; it cannot target the collection the API key signs in with, or a media collection. A channel publishes to one CMS. Saving reads from the customer's CMS and re-checks the connection. Admins only. Switching to live mode, the API key and the site URL are changed in the Mark app only. Related: get_website_cms, sync_connections (test, or retry a failed publish).
Inputs: mapping, provider, rediscover, publishMode, +1 more. Returns: cms, appUrl
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceRemote MCP server that lets AI assistants read tracked competitive intelligence — competitor news, reviews, website changes, social posts and ads — and act on it by managing projects, competitors, battle cards, reports and canvases. Every answer carries a source and a date, and write actions only publish when explicitly shared.MIT
- AlicenseCqualityCmaintenanceCompetitor Monitor AI - MCP server providing AI-powered tools and automation by MEOK AI Labs117 npm46 PyPIMIT
- AlicenseAqualityDmaintenanceEnables brand visibility monitoring across major AI platforms like ChatGPT, Claude, Gemini, and Perplexity. It allows users to track visibility scores, analyze competitor data, and receive actionable insights to improve AI-generated brand recommendations.169 npm1MIT

industrylens-mcpofficial
AlicenseNot gradedqualityCmaintenanceBrowse IndustryLens's published competitive-intelligence reports and head-to-head competitor comparisons from any AI agent — real, source-backed data.MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.