| list_skillsA | List all available Unilytics workflow skills — battle-tested, multi-step marketing workflows. Returns a catalog of skills grouped by category. Use get_skill() to load
the full workflow recipe for any skill, then follow its steps using the
available MCP tools.
Categories: seo, content, ads, analytics, action, enterprise, geo, cua
|
| get_skillA | Load a specific workflow skill recipe — a structured, multi-step guide for Claude to follow. The recipe includes step-by-step instructions, which tools to call, what data to gather,
and how to present results. Use this as a guide, not a rigid script — adapt steps based
on the user's actual needs.
Args:
skill_id: The skill identifier (e.g. "seo/keyword_research", "content/blog_topic_ideas", "ads/google_ads/standard_campaign")
|
| author_skillA | Read this BEFORE authoring a new skill with create_skill — how to structure it well. Returns the Unilytics skill-authoring best practices (progressive disclosure,
gotchas, sub-agents, measured-not-claimed gates, trigger descriptions, the
self-learning loop, and the VERBATIM-import rule) plus a starter SKILL.md
template. Grounded in how Claude Code's own skill system works. Call this when
the user asks you to create, port, or refactor a skill.
|
| read_skill_fileA | Load one reference/data file of a skill, on demand (progressive disclosure). get_skill() returns the SKILL.md contract + the list of its reference_files
and data_files. Call this to load a specific one (e.g. "reference/keyword-
pipeline.md" or "data/verify_row.py") only when you reach the step that needs
it — keeps context lean. For a stored script, read it then run it.
Args:
skill_id: The skill id (user/... or project/... for editable skills).
path: The file path within the skill (e.g. "reference/competitor-analysis.md").
`file_path` is accepted as an alias (matches the file tools).
|
| create_skillA | Create a new editable skill (a SKILL.md + optional reference/data files come later via update_skill). Use this to author a skill from Claude, or to CUSTOMIZE a built-in (pass
extends="<builtin_skill_id>" and the built-in's body is seeded for you).
FILE LAYOUT (a skill is SKILL.md + supporting files — progressive disclosure):
- SKILL.md the always-loaded contract: workflow + definition-of-done.
Keep it lean; link to the files below, don't inline them.
- reference/*.md docs the agent READS on demand (style guides, pipelines).
- data/* assets the skill carries, including RUNNABLE scripts
(e.g. data/verify_row.py — a checker the agent reads and
EXECUTES as a gate). Put scripts and data files here, NOT
under reference/, so get_skill surfaces them as runnable.
Args:
name: Skill name (becomes the slug). Required.
skill_markdown: The SKILL.md contents (frontmatter + workflow). Required
unless `extends` is given (then it's seeded from the built-in, but you
may override).
description: Short description (else taken from frontmatter).
category: Category label (e.g. "seo", "content").
scope: "user" (private to you) or "project" (shared with collaborators).
project_id: Required when scope="project".
extends: A built-in skill_id to copy as the starting point (Customize).
visibility: "private" | "project" | "public".
verbatim_source: When PORTING an existing document, pass the original text
here. The store runs a coverage-based fidelity check and returns a
report — a FAIL means content was SILENTLY CONDENSED (do not report
"done"; re-port the missing segments). Splitting + intentional edits
are allowed. Call author_skill() first for the full authoring guide.
|
| update_skillA | Save changes to an editable skill — writes a new version (no publish step). Three composable ways to change files in ONE version. Prefer file_edits for
small changes — it sends only the changed text, not the whole file.
file_edits — surgical find/replace on an EXISTING file. Maps path ->
list of edits, e.g.
{"reference/style-guide.md": [{"find": "—", "replace": "-", "count": 5}]}
Each `find` must match exactly `count` times (default 1); a 0-match or a
count mismatch is rejected so you fix the string rather than write blind.
Use a longer, unique `find` for a single edit, or set `count` to replace
all occurrences. THIS IS THE CHEAP PATH for fixing a few characters.
file_changes — create a new file or whole-file replace. Maps path ->
full markdown, e.g. {"SKILL.md": "...", "reference/new.md": "..."}.
Use for new files or large rewrites.
delete_files — remove files, e.g. ["reference/old.md"]. SKILL.md can't be
deleted (it is the contract).
FILE LAYOUT (where files go — get_skill surfaces them by these prefixes):
- reference/* docs the agent READS (style guides, pipelines, specs).
- data/* assets the skill CARRIES, incl. runnable scripts. Put a
checker like data/verify_row.py HERE (not reference/) so it
travels with the skill and get_skill lists it as runnable —
the agent then read_skill_file()s it and EXECUTES it as a gate.
Built-in skills are read-only — update is rejected; create_skill(extends=...)
first, then update that copy.
Args:
skill_id: The editable skill id (user/... or project/...).
file_changes: {path: markdown} — create/replace whole files.
file_edits: {path: [{find, replace, count?}]} — surgical edits to existing files.
delete_files: [path, ...] — files to remove.
change_summary: Short note on what changed (for version history).
expected_version: If set, the save fails with a conflict unless the
skill is still at this version (safe concurrent editing).
verbatim_source: When PORTING/refactoring existing content, pass the
original text. The store returns a fidelity report on the resulting
files — a FAIL means content was silently condensed (re-port the
missing segments; don't claim done). Splitting + intentional edits OK.
|
| delete_skillA | Archive an editable skill (soft delete). Built-in skills cannot be deleted. Args:
skill_id: The editable skill id (user/... or project/...).
|
| get_skill_memoryA | Read a skill's accumulated MEMORY (learnings/workarounds from prior runs). Call this at the START of running a skill — it carries durable lessons,
workarounds, and user preferences that make each run better than the last.
Args:
skill_id: The skill id (built-in like "seo/keyword_research", or user/project).
|
| append_skill_memoryA | Save a learning to a skill's MEMORY — the self-learning loop. After completing a skill, ASK THE USER whether anything failed, could be done
better, or is worth remembering for next time. On their confirmation (they may
add items), append it here. Works for built-in skills too — the learning lives
on the per-skill memory, never modifying the built-in body.
Args:
skill_id: The skill the learning belongs to.
text: The learning/workaround/preference to remember (one concise entry).
|
| compact_skill_memoryA | Compact a skill's MEMORY into one summary entry (when it gets large). Call this only when append_skill_memory returns memory_full. Read the memory,
distill the durable lessons into a concise summary, and pass it here — it
replaces the old entries with your summary.
Args:
skill_id: The skill whose memory to compact.
summary: The distilled summary of durable lessons to keep.
|
| seo_domain_overviewA | Get comprehensive SEO overview for a domain including organic traffic, keywords count, and visibility. Args:
domain: Domain to analyze (e.g. "example.com")
country: Two-letter country code (default: "us")
|
| keyword_researchA | Research a keyword: search volume, CPC, competition, difficulty, and trends. Combines Serpstat keyword overview with Keywords Everywhere volume data.
Args:
keyword: Keyword to research (e.g. "best crm software")
country: Two-letter country code (default: "us")
|
| keyword_rankingsA | Get all keywords a domain ranks for with positions, volume, and URLs. Args:
domain: Domain to check rankings for (e.g. "example.com")
country: Two-letter country code (default: "us")
page: Results page number (default: 1, 50 results per page)
|
| backlink_analysisB | Analyze a domain's backlink profile: referring domains, domain authority, spam score. Combines Serpstat backlink overview with Moz domain authority data.
Args:
domain: Domain to analyze backlinks for (e.g. "example.com")
|
| competitor_analysisA | Find a domain's organic search competitors and their overlap metrics. Args:
domain: Domain to find competitors for (e.g. "example.com")
country: Two-letter country code (default: "us")
|
| page_speed_auditA | Audit a page's loading performance: Core Web Vitals, performance score, opportunities. Args:
url: Full URL to audit (e.g. "https://example.com")
strategy: "desktop" or "mobile" (default: "desktop")
|
| technical_seo_auditB | Run a technical SEO audit on a domain or specific URL. If a URL is provided, also checks structured data (schema.org markup).
Args:
domain: Domain to audit (e.g. "example.com")
url: Optional specific URL to also check for schema markup
|
| serp_resultsA | Get current Google search results for a keyword (top 20 organic results). Args:
keyword: Search query (e.g. "best project management tools")
location_code: Google Ads location code (default: 2840 = United States)
|
| on_page_auditA | Audit a specific page's on-page SEO: title, meta, headings, content quality. Args:
url: Full URL to audit (e.g. "https://example.com/page")
|
| top_pagesA | Get a domain's top-performing pages by organic traffic. Args:
domain: Domain to check (e.g. "example.com")
country: Two-letter country code (default: "us")
|
| competitor_adsA | Get a competitor's Google Ads copy — headlines, descriptions, and target keywords. Args:
domain: Competitor domain (e.g. "competitor.com")
country: Two-letter country code (default: "us")
|
| content_scrapeB | Scrape a web page and extract its content, headings, and structure. Args:
url: Full URL to scrape (e.g. "https://example.com/blog/post")
include_metadata: Include meta tags and OpenGraph data (default: False)
|
| web_research_toolA | Research a topic using AI-powered web search. Returns synthesized findings. Args:
query: Research query (e.g. "latest SEO trends 2026")
research_type: One of "general", "news", "brand_research", "competitor_audit"
|
| google_trendsA | Compare keyword interest over time using Google Trends data. Args:
keywords: List of keywords to compare (e.g. ["chatgpt", "gemini", "claude"])
time_range: Time period — "past_7_days", "past_30_days", "past_90_days", "past_12_months", "past_5_years"
|
| social_profileA | Scrape a social media profile: followers, bio, and recent posts/videos. Supports Facebook pages, Instagram profiles, YouTube channels, and Twitter/X profiles.
Args:
url: Profile URL (e.g. "https://facebook.com/nike")
platform: One of "facebook", "instagram", "youtube", "twitter"
max_posts: Maximum recent posts to fetch (default: 10)
|
| brand_visibilityA | Check how a brand appears in AI-generated answers (ChatGPT, Claude, Perplexity). Queries multiple AI models with the same prompt and analyzes brand mentions.
Use this for Generative Engine Optimization (GEO) analysis.
Args:
prompt: The question to ask AI models (e.g. "What is the best CRM for startups?")
brand: Optional brand name to track in responses (e.g. "HubSpot")
|
| list_projectsA | List your Unilytics projects — shows all projects you own or have access to. Call this first to discover your project_id, then use list_connections()
to see connected data sources, and search_console_data() to query your data.
|
| list_connectionsA | List connected data sources for a project (GSC, GA4, Google Ads, etc.). Call list_projects() first to get your project_id.
Args:
project_id: The project UUID from list_projects()
|
| search_console_dataA | Query your Google Search Console data — keywords, pages, clicks, impressions, and rankings. Requires a connected GSC data source. Call list_projects() then list_connections()
to find your project_id and account_id.
Args:
project_id: Your project UUID (from list_projects)
account_id: GSC account ID (from list_connections, e.g. "sc-domain:example.com")
endpoint: What data to fetch:
- "top_pages": Top organic pages with clicks (last 3 months)
- "queries": All search queries
- "ranking_keywords": Keywords with click/impression metrics
- "clicks_impressions": Click and impression data over time
- "complete_data": Full GSC data with all dimensions (last 2 months)
|
| get_credit_balanceA | Check your Unilytics credit balance and API usage. Call this before running expensive workflows to make sure you have enough
credits. Each data tool call costs 0.10 credits. Discovery tools (list_projects,
list_connections, list_skills, get_skill) are free.
|
| analytics_dataA | Query your Google Analytics 4 data — sessions, users, conversions, traffic sources, and device breakdown. Requires a connected GA4 data source. Call list_projects() then list_connections()
to find your project_id and account_id.
Args:
project_id: Your project UUID (from list_projects)
account_id: GA4 property ID (from list_connections, e.g. "375869549")
endpoint: What data to fetch:
- "complete_data": Full analytics with device breakdown (last 2 months)
- "sessions": Session and user metrics (last 3 months)
- "conversions": Conversion and e-commerce metrics (last 3 months)
- "device_breakdown": Metrics by device category (last month)
- "traffic_acquisition": Traffic by source/medium (last 2 months)
|
| google_ads_dataA | Query your own Google Ads account data — clicks, impressions, campaigns, keywords, and ad copy. Requires a connected Google Ads data source. Call list_projects() then list_connections()
to find your project_id and account_id.
Args:
project_id: Your project UUID (from list_projects)
account_id: Google Ads account ID (from list_connections, e.g. "123-456-7890")
endpoint: What data to fetch:
- "clicks_impressions": Click and impression totals (all time)
- "campaign_performance": Campaign metrics with spend (last 3 months)
- "ad_performance": Ad-level metrics with CPC (last month)
- "complete_data": Full Google Ads data with all key metrics (last 2 months)
- "keywords": Keyword performance — text, match type, clicks, impressions
- "ad_copy": Ad creative copy — headlines, descriptions, final URLs
|
| keyword_plannerA | Generate keyword ideas with search volume from Google Ads Keyword Planner. Takes seed keywords and returns related keyword ideas with monthly search
volumes, competition level, and trends. Uses the official Google Ads API.
Args:
keywords: Seed keywords to generate ideas from (e.g. ["seo tools", "digital marketing"])
|
| google_ads_transparencyA | See what Google Ads a competitor is running via the Ads Transparency Center. Returns ad creatives (text, image, video), target domains, and links to
the Google Ads Transparency Center for each ad.
Args:
advertiser_name: Brand domain to search (e.g. "nike.com", "apple.com"). Use domain, not just brand name.
region: Region code (default: "2840" = US). Common: "2356" India, "2826" UK, "2124" Canada, "2036" Australia.
limit: Max ad creatives to return (default: 30, max: 100)
|
| meta_ads_libraryA | Search the Meta Ads Library for competitor ads on Facebook and Instagram. Returns active ad creatives, spend ranges, and targeting info.
Args:
query: Brand or keyword to search (e.g. "Nike", "best crm software")
country: Two-letter country code (default: "US")
limit: Max results to return (default: 25)
|
| audience_segmentsA | Browse Google Ads audience segments for campaign targeting. Returns available audience segments filtered by type and optional search query.
Args:
segment_type: Segment type — "IN_MARKET", "AFFINITY", or "LIFE_EVENTS"
query: Optional filter (e.g. "fitness", "travel") to narrow results
|
| audience_recommendationsA | Get AI-recommended audience signals for a Google Ads campaign. Based on your keywords and industry, recommends in-market segments,
affinity audiences, demographics, and bid modifiers.
Args:
keywords: Target keywords for the campaign (e.g. ["hotels in goa", "beach resorts"])
campaign_type: "standard" or "pmax" (Performance Max). Default: "standard"
industry: Business industry (e.g. "travel", "ecommerce", "saas"). Default: "general"
|
| demographic_optionsA | Get all available Google Ads demographic targeting options. Returns age ranges, genders, parental status, and household income
tiers with their Google Ads API enum values. Useful for building
campaign targeting criteria. |
| js_scrapeA | Scrape a JavaScript-rendered page (SPAs, React, Vue, Angular apps). Unlike the standard scrape, this waits for JavaScript to fully execute
before extracting content. Use this for single-page applications, dynamic
content, or any page where content loads via JS.
Returns full SEO audit signals from the rendered HTML: title, meta tags,
headings, Open Graph, structured data, canonical, images, and issues.
Args:
url: Full URL to scrape (e.g. "https://app.example.com")
wait_for: Milliseconds to wait for JS to render (default: 5000, max: 15000)
|
| batch_scrapeA | Scrape multiple URLs in a single call (up to 10 URLs). Firecrawl processes all URLs in parallel — much faster than scraping one
by one. Use for competitor analysis, content gap analysis, SERP page
comparisons, link prospecting, or any task requiring data from multiple pages.
Returns markdown content and metadata for each URL.
Args:
urls: List of URLs to scrape (max 10)
js_rendering: Wait for JavaScript rendering on each page (default: False)
|
| site_crawlA | Crawl a website and return data for every discovered page (up to max_pages). Recursively follows internal links from the starting URL. Use for site-wide
audits, content inventory, internal link analysis, finding orphan pages,
detecting missing meta tags across a site, or any task needing full-site data.
Returns per-page SEO signals: title, meta description, headings, issues.
Args:
url: Starting URL (e.g. "https://example.com")
max_pages: Maximum pages to crawl (default: 50, max: 100)
|
| site_mapA | Discover all URLs on a website without scraping their content. Fast URL discovery that maps out a site's structure. Use for sitemap
audits (compare against sitemap.xml), finding unindexed pages, understanding
site architecture, or scoping a crawl before running it.
Args:
url: Website URL (e.g. "https://example.com")
|
| page_screenshotA | Capture a screenshot of a webpage. Takes a rendered screenshot after JavaScript execution. Use for visual
audits, above-the-fold analysis, competitor page comparison, UX review,
or documenting current page state.
Args:
url: Full URL to screenshot (e.g. "https://example.com")
full_page: Capture full scrollable page vs viewport only (default: False)
|
| link_extractA | Extract and classify all links from a webpage. Returns internal vs external links with anchor text, nofollow attributes,
and link counts. Use for link audits, outbound link analysis, internal
linking reviews, finding link building opportunities on competitor pages,
or checking nofollow distribution.
Args:
url: Full URL to extract links from (e.g. "https://example.com/page")
js_rendering: Wait for JavaScript rendering before extracting (default: False)
|
| create_projectA | Create a new Unilytics project for the authenticated user. Use this to set up a project from Claude — then add tracked keywords/prompts
and upload files to it. Returns the new project_id (use it in later tool calls).
Args:
name: The project / brand / business name (required).
domain: The project's website URL (optional, e.g. "example.com").
keywords: Keywords to track — a list, or a comma-separated string.
competitors: Competitor domains — a list, or a comma-separated string.
industry: The project's industry (optional).
|
| add_tracked_keywordsA | Add tracked keywords, competitors, and/or target locations to a project. Appends to the project's existing lists (no duplicates). Call list_projects()
first to get the project_id. At least one of keywords/competitors/locations
must be provided.
Args:
project_id: The project UUID (from list_projects or create_project).
keywords: Keywords to add — a list, or a comma-separated string.
competitors: Competitor domains to add — a list, or comma-separated string.
locations: Target locations to add — a list, or comma-separated string.
|
| add_tracked_promptsA | Add AEO prompts to track for a project (questions sent to AI answer engines). EXPECTED SHAPE — each prompt is an object with at minimum a `text` field:
{"text": "best managed file transfer tools", "topic": "MFT",
"tags": ["mft"], "prompt_type": "category"}
Only `text` is required. Optional: topic, tags (list or comma-string),
prompt_type ("brand"|"category", default "category"), regions, language,
personas, platforms, analysis_types.
If you have a sheet/CSV, MAP its columns to this shape before calling
(e.g. a "Prompt" column -> text, "Topic" -> topic, "Tags" -> tags). If this
tool returns a structured error, fix the data per the error and retry.
Args:
project_id: The project UUID (from list_projects or create_project).
prompts: List of prompt objects (see shape above).
|
| create_folderA | Create a folder (Context) inside a project to organize skills + files. A folder groups a related set of skills (e.g. a "Content Refresh" folder),
its knowledge-base files, and its memory — the project's working context.
Call list_projects() first for the project_id.
Args:
project_id: The project UUID (from list_projects or create_project).
name: The folder name (e.g. "Content Refresh").
|
| list_foldersB | List the folders (Contexts) in a project. Args:
project_id: The project UUID (from list_projects).
|
| list_folderA | List a folder's contents — its skills (by reference) first, then its files. Args:
folder_id: The folder ID (from list_folders or create_folder).
|
| add_skill_to_folderA | Attach a skill to a folder by reference (the skill's files stay in the skill store). This does NOT copy skill files — it records that this folder contains the skill.
Call list_folders() for folder_id; skill_id comes from the skill store (Phase 0/1).
Args:
folder_id: The folder ID (from list_folders or create_folder).
skill_id: The skill ID to attach.
|
| aeo_cluster_by_urlsA | Cluster prompts by shared Google SERP URLs, then assign each cluster a page. Greedy centroid assignment on an ABSOLUTE shared-URL count (no transitive chaining). A join on
exactly one shared URL is allowed only if that URL is specific (low corpus document frequency),
is not a generic hub host, and the prompt is not a brand comparison — and such joins are NEVER
auto-accepted (they come back with needs_review=true). Query-drift SERPs are quarantined.
Page assignment is rank-weighted: a rank-1 brand hit outweighs a rank-10 one.
items = [{id, text, impressions?, serp: [{url, rank, near_miss}], serp_urls?: [...] (legacy),
citation_urls?: [...] (a SEPARATE signal — never joins clusters), spell?, mapped_pages?}]
config = per-project {brand:{domain}, competitors[], cluster:{T, one_shared_max_df, hub_hosts,...},
signals:{a_min_hits, a_high_confidence_max_rank}} — nothing client-specific is hardcoded.
Returns {clusters, assignments, n_items, n_clusters, needs_review, n_quarantined, caveat}.
|
| aeo_cluster_promptsA | Group prompts into topic clusters by text embedding similarity. items = [{id, text, intent?,
volume?}]. Returns {clusters, n_items, n_clusters}. |
| aeo_audit_promptsA | Audit a tracked-prompt set — per-prompt intent, realistic metric, play, and a keep/cut/
rewrite recommendation. prompts = [{id, text, intent?, mention_rate?, citation_rate?}]. |
| aeo_filter_promptsA | Clean an imported keyword/prompt list — remove junk (operators, year-stamps, foreign, geo,
brand-variants, other-brands, dupes). items = [{text}] or [str]. Returns {kept, removed, summary}. |
| aeo_serp_urlsA | Top Google organic results for a keyword/prompt, WITH RANK. Returns {results: [{url, rank, near_miss}], urls: [top-N urls], spell}. Fetches the top-20;
`near_miss` marks ranks beyond top_n (a real ranking, but weak evidence — use it to find
refresh candidates, not to claim the page ranks). `spell` carries Google's did_you_mean /
showing_results_for so callers can detect query drift.
|
| aeo_site_pagesA | Discover a site's sitemaps via robots.txt. Returns {site, sitemaps:[{url,url_count,is_index}]}. |
| aeo_generate_promptsA | Propose additional natural-language AEO prompts for a topic, intent-balanced + deduped. |
| aeo_prompt_volumeB | AI search-volume estimate for a prompt (expensive multi-API pipeline). Returns {volume, bucket}. |
| kb_listA | List a project's Knowledge Bases (collections) with file counts + sizes. |
| kb_manageA | Create a Knowledge Base (collection) in a project. KBs are not auto-created on write —
create one explicitly, then write documents into it by its id. |
| kb_write_textA | Write an inline text/markdown document into a KB (research summary, agent output, notes).
Returns {document_id, status}. Ingest embeds the content so it becomes searchable. Poll
kb_get_status; search returns the doc once status is 'ready'. metadata is a single-level
key/value hash, filterable via kb_search. |
| kb_add_urlsA | Add one or more webpage URLs to a KB — each is scraped + ingested as its own document.
Returns a job id; poll kb_get_status for per-document completion. The same metadata hash is
applied to every document. |
| kb_create_direct_uploadA | Step 1 of a two-step file upload: get a presigned URL to upload a file into a KB. Returns
{signed_id, upload_url, content_type}. PUT the file bytes to upload_url with the returned
content_type as the Content-Type header, then call kb_add_file with the signed_id. Allowed:
PDF/DOCX/TXT/MD/RTF/PPT(X)/CSV/HTML/EML/EPUB/XLSX (max 256MB). |
| kb_add_fileA | Step 2 of a two-step file upload: after PUTting the file to the upload_url from
kb_create_direct_upload, create the KB document from the uploaded file (signed_id). The file's
text is extracted, chunked, and embedded. Returns {document_id, status}; poll kb_get_status. |
| kb_searchA | Semantic search across a project's KB. Write the query in natural language (the same
language as the documents). Optionally scope to one KB or filter by metadata. Returns the
top matching chunks with their source + score. |
| kb_get_statusA | Get a KB's indexing status — total/ready/pending/failed counts + per-document status.
Call this after any write; search only returns 'ready' documents. |
| kb_update_document_metadataA | Replace a document's metadata (single-level key/value hash) in full — no merge, no re-embed.
To remove a key, pass the full new hash without it. Filterable via kb_search. |
| kb_delete_documentA | Permanently delete one document from a KB (drops its indexed chunks). DESTRUCTIVE — confirm
with the user before calling. Idempotent: deleting an already-deleted doc is a no-op. |
| kb_get_documentA | Read a KB document's full extracted content + metadata (not just search chunks). Use to pull
a known document into context or read a previously-written artifact end-to-end. |
| kb_update_documentA | Refresh a document's content in place, keeping the SAME document_id (so references stay
valid). Pass a url to re-fetch a URL doc, or content for a text doc. Status goes
ready→pending→ready. Note: a resync rebuilds the index — re-apply agent metadata afterward. |
| kb_deleteA | Permanently delete a Knowledge Base and ALL its documents. DESTRUCTIVE — confirm with the
user before calling. Idempotent. |