Verify the MCP pipe; echoes a message and returns the resolved org id, org name and actor type.
Tools your agent can call.
Munin exposes 216 tools at /mcp. Audiences gate which tokens see which tools — admin keys see everything, delegated end-user tokens see only self-service tools.
Connect a client
Wire your favourite model to Munin in a couple of minutes.
Admin tools 204
Available to admin API keys and admin sessions. Includes everything that writes or that reads across the org.
Find the end-user identity that an email address, phone number, external id, or analytics visitor id belongs to. An end user is the durable per-person record created the first time someone reaches the org on any channel — inbound email, chat widget, or an analytics identify call — so it exists for people who have no CRM contact yet. Returns `endUserId` plus `matchedOn` naming which identifier matched, and `crmContactId` (null when no CRM contact has been created for this person). Returns `endUserId: null` when nothing matches; it never creates a record. Match order is external id, then email, then phone, then visitor id.
Input schema
Read one end user with a cross-channel summary: which channel types they have written on, how many conversations they have and when the last one was, their linked analytics visitor ids, their page-view and search event counts, and the ids of their CRM contact and conversation contact if those exist. Use it to see everything the org already knows about a person before answering them. Covers only data stored in Munin — orders and bookings live in the customer's own systems and are read separately by email.
Input schema
Export this org's analytics configuration — trackers and visitor-identity links — as a portable JSON payload. Low-volume, returned in one shot. Tracker identity-verification secrets are redacted (the ciphertext is useless on another server); the operator re-enters them after import. Pair with `analytics_export_events` (paginated) and feed both into `analytics_import` on another Munin server.
Input schema
Export this org's analytics events (page-view and search events) as a portable JSON payload. High-volume, so this is keyset-paginated over (createdAt, id): call with no arguments for the first page, then pass the returned `nextCursor` back as `cursor` until it comes back `null`. `limit` defaults to 200 (max 500). Feed each page's `records` into `analytics_import`. Import trackers + visitor identities first via `analytics_export_config` so event foreign keys resolve.
Input schema
Import analytics `config` (trackers + visitor identities) and/or `events` (view + search events) produced by `analytics_export_config` / `analytics_export_events`, typically from another Munin server. Trackers are upserted by (org, name); visitor identities by (org, visitorId). Trackers import without their redacted identity-verification secret — rotate it afterwards. Visitor identities and events resolve their end-user / tracker foreign keys through `idMap`, so import end-users first and pass that `idMap` back in here. Events have no natural key: they are de-duplicated only within one run via `idMap` (re-running without the prior `idMap` inserts duplicates). Returns counts, warnings, and the merged `idMap` (source id → id on this server) — pass it forward to later imports.
Input schema
Create a tracker and mint a public `mn_track_*` API key bound to it. The key is safe to embed in `<script>` tags or mobile clients — it can only write page-view events scoped to your org, never read them. `allowedOrigins` is an optional list of full origins (`https://example.com`) the tracker will accept; when empty, any origin is accepted (set `MUNIN_TRACKER_REQUIRE_ALLOWLIST=1` to fail-closed instead). Returns the plaintext key once; store it where it needs to be embedded. Scaffolding a frontend from Lovable/Bolt/v0/Replit/Cursor? Read `skill://playbooks/frontend-integration` first — it covers the tracker + widget + CMS wiring end-to-end.
Input schema
List analytics trackers for the current org with their key prefix, allowed origins, and revocation state. Plaintext keys are never returned; rotate via `analytics_revoke_tracker` + `analytics_create_tracker`.
Input schema
Update a tracker's display name, `allowedOrigins`, or `canonicalLocales`. Ingest already folds trailing slashes and a leading path segment that matches the page's own `lang` attribute, so `canonicalLocales` is only needed when a URL prefix disagrees with that tag (a `/no/…` path on pages declaring `lang="nb-NO"`) or when pages set no `lang` at all; entries are matched against the first path segment, and subject ids that aren't path-shaped are never rewritten. Takes effect on the next event — no site redeploy — and past rows keep the ids they were written with. The bound API key is unchanged — rotate via `analytics_revoke_tracker` + `analytics_create_tracker`.
Input schema
Mint a fresh HMAC secret for verifying visitor-identity claims sent to `/v1/a/identify`. Returns the plaintext secret once; store it server-side and use it to sign the identity payload — the fields `['mn.identity.v1', externalId, visitorId, email]`, each prefixed with its UTF-8 byte length, concatenated. The hash binds the visitor, so read `visitorId` from `window.mn.analytics.getVisitorId()` first, then call `window.mn.analytics.identify(externalId, userHash, { email })` from the browser. The email is optional and may be omitted from both the payload and the call, but including it is what links a visitor to the identity the email channel created for the same address. See `skill://analytics/identify-visitors`. The previous secret is replaced immediately — any in-flight identify calls signed with it will fail.
Input schema
Revoke any active `mn_track_*` keys bound to this tracker and mint a fresh one. Returns the new plaintext key once; update any page that embeds the old key. Pages still embedding the old key will silently stop recording views once revocation lands.
Input schema
List the most-viewed subjects (CMS entries, landing pages, etc.) over a recent window. Use this to see what content is actually getting traffic. Filter by `subjectType` to scope to one surface (e.g. `cms_entry`). Pass `trackerId` (from `analytics_list_trackers`) to report on a single site or app instead of every tracker in the org summed together. Pass `endUserId` or `contactId` to restrict the ranking to one identified visitor — useful for "what has this lead been reading?".
Input schema
Visitor and view counts grouped by ISO 3166-1 alpha-2 country code over a recent window. Requires the backend to have `MUNIN_GEOIP_DB_PATH` configured; rows recorded without a GeoIP DB carry `country = NULL` and roll up into an "unknown" bucket. Filter by `subjectType` (e.g. `page`, `cms_entry`), `source`, or `trackerId` (from `analytics_list_trackers`, to report on a single site or app instead of every tracker summed together) to scope.
Input schema
Views and unique visitors grouped by `utm_source` (with `utm_medium` / `utm_campaign` breakdown). Use this to compare campaign attribution: which channels actually drive engaged traffic vs. just clicks. Rows where `utm_source` is NULL (no campaign params on the URL) roll into a single "direct/organic" bucket. Pass `trackerId` (from `analytics_list_trackers`) to attribute one site or app on its own instead of every tracker in the org summed together.
Input schema
External traffic sources grouped by the host portion of `referrer`. Use this to see which sites are linking to you (HN, Reddit, partner blogs). Same-origin referrers are excluded server-side via the `excludeHost` argument (typically your own production host); pass it to keep internal navigations from drowning out external referrals. Rows with NULL referrer (direct navigation, bookmarks, link-with-`rel=noreferrer`) roll into a single "direct" bucket. Pass `trackerId` (from `analytics_list_trackers`) to scope to one site or app instead of every tracker in the org summed together — which also makes `excludeHost` meaningful when the org runs several domains.
Input schema
Daily view + unique-visitor counts over a recent window. Returns one row per UTC day, ordered oldest → newest, with zero-filled gaps so days with no traffic appear as `views: 0`. Use this to spot trends, weekly patterns, and the impact of campaigns or content launches. Pass `trackerId` (from `analytics_list_trackers`) for one site or app on its own; without it every tracker in the org is summed into one line. In hosts that support MCP Apps this renders an inline time-series chart.
Input schema
View counts, unique visitors, and average dwell/read-depth for one subject (e.g. one CMS entry) over a recent window. Use this when judging whether a stale entry should be refreshed or archived. Pass `trackerId` (from `analytics_list_trackers`) when the same subject id is served by more than one tracked site and you want them apart.
Input schema
Compute a conversion funnel over page-view events: how many distinct visitors reached each ordered step, and where they drop off. Pass 2–8 `steps`; each step matches a view event by `subjectType` and/or `subjectId` (e.g. `{ subjectType: "page", subjectId: "/pricing" }`) and/or a `pathLike` SQL LIKE pattern (e.g. `{ pathLike: "/blog/%" }`). Steps are strictly ordered — a visitor counts at step N only if they hit step N after reaching step N-1. Visitors are grouped by a stable actor key (their identified end-user when known, else their anonymous `visitor_id`), so a journey that spans the anonymous → identified transition is not double-counted. Set `stepWindowHours` to require each step to follow the previous within a time budget (e.g. signup within 24h of viewing pricing). Pass `trackerId` (from `analytics_list_trackers`) to build the funnel from one site or app only; without it, steps match events from every tracker in the org. Anonymous funnels work without any identity setup. Returns per-step actor counts plus conversion/drop rates.
Input schema
Chronological list of page-view and search events recorded for one identified visitor. Pass either `contactId` (resolved through `crm_contacts.endUserId`) or `endUserId` directly. Returns the ordered event timeline — what the lead looked at before they reached out, what they searched for, etc. Visitors are linked to an end-user identity by the chat-widget on first chat, or via `window.mn.analytics.identify(externalId, userHash, { email })`. Events recorded under a `visitor_id` *before* that link was established are still included retroactively — the link is resolved at read time — so the journey spans the visitor's anonymous history too. Pass `trackerId` (from `analytics_list_trackers`) to narrow the timeline to one site or app.
Input schema
List recent public search queries that returned zero results. The single best input for "what should we write about next" — readers are asking but Munin has no answer. Pass `trackerId` (from `analytics_list_trackers`) to see one site's or app's searches on their own; searches Munin ran itself through the CMS delivery API carry no tracker and are excluded by that filter.
Input schema
Revoke the API key bound to a tracker. After this, the key is rejected by the ingest endpoints — any pages still embedding it will silently fail to record views. The tracker row stays for audit.
Input schema
List CMS collections (content types) defined for your org. Scaffolding a frontend from Lovable/Bolt/v0/Replit/Cursor and need to render CMS content? Read `skill://playbooks/frontend-integration` — the delivery API is anonymous and intentionally has no CORS, so the fetch must run server-side.
Input schema
Read one collection by id or slug, including its field definitions.
Input schema
Define a new collection. Fields is an ORDERED array of { name, type, required?, options? } — field order is the render order in editor and public surfaces, so put fields in the sequence a human would read or fill them (e.g. lede asset → headline → excerpt → metadata → body → trailing/optional fields). See field types: text, rich_text, markdown, number, integer, boolean, date, datetime, select, multi_select, asset, reference, array, json.
Input schema
Patch a collection. When supplied, `fields` REPLACES the existing array — so include every field you want to keep, in the order they should render (the array order is the render order). Field migration is lossy: dropped or renamed fields stay in entries' `data` jsonb but stop being read by the projection layer. `localized` can be turned on at any time and existing entries keep the locale they already have; turning it off is refused while the collection holds entries in more than one locale.
Input schema
Delete a collection. Cascades to all entries, versions, and references.
Input schema
List entries as summaries: identifiers, status, derived title, short fields verbatim, and long text shortened to a lead with a word count in `fieldSummary`. Pass `ids` to read up to 50 specific entries in one call, or `fields` to return named fields verbatim. `truncated: true` marks an entry whose full field values were withheld from the summary. Filters: collection (id or slug), status, locale. Drafts and scheduled entries are returned to admins; the public delivery API only ever returns published.
Input schema
Read one entry in full, including complete long-text fields. Data is projected through the collection's current field schema.
Input schema
Mint a signed, short-lived (1 hour) preview link for one entry so a draft can be viewed on the customer frontend before publishing. Returns `url` (the collection's settings.previewUrl template with {slug}, {locale}, {token}, {collection} substituted; null when no template is configured) and `deliveryUrl` (the raw delivery-API JSON URL with ?preview=).
Input schema
Create a new entry in a collection. `data` is keyed by field name; required fields must be present. Pass `status: "published"` to publish on creation; default is draft. Published entries are stamped with `publishedAt` — pass one to preserve the original date when migrating existing content. Slugs are unique per (collection, slug, locale), so each locale can have its own slug — when the entry is a translation of an existing one, pass `translationOf` with that entry's id to link them.
Input schema
Update an entry. Pass `ifVersion` (the current version you read) for optimistic concurrency. `data` is a partial patch — keys you send replace the corresponding keys on the existing entry; keys you omit are preserved. Pass an explicit `null` to clear a single key. The merged payload is then re-validated against the collection schema, and search_text + embedding + references are regenerated.
Input schema
Flip an entry to status="published". Stamps publishedAt (now, or the `publishedAt` you pass for migrated content) and fires cms.entry.published.
Input schema
Revert an entry to status="draft". Clears publishedAt; fires cms.entry.unpublished.
Input schema
Schedule an entry to flip to published at a future ISO 8601 datetime. The schedule worker drains due rows every minute.
Input schema
Delete an entry. Cascades to its versions and references.
Input schema
List every locale variant of one entry — the entries sharing its translation group — with each variant's own slug, status and id. Each locale carries its own slug, so this is how you find the Norwegian URL of an English entry, build a language switcher, or see which locales are still missing.
Input schema
Move an entry into the translation group of another entry in the same collection, so the two are recognized as locale variants of the same content. Use it for entries that were authored separately and should have been linked; entries created with `translationOf` are already linked. Conflicts if the target group already has an entry in this entry's locale.
Input schema
Detach an entry from its translation group and give it a fresh group of its own. Its former siblings keep their group; content, slug and locale are untouched. Use it to undo an incorrect link.
Input schema
List all prior versions of an entry, newest first.
Input schema
Roll an entry back to an earlier version. Creates a new current version with that historical data.
Input schema
List media-library assets in your org. In hosts that support MCP Apps this renders a thumbnail gallery with per-asset usage lookup.
Input schema
Mint a presigned upload for a new asset. Only usable from clients that can issue raw HTTP PUT/POST themselves. If your runtime has no client-side PUT primitive, this tool is not a fit — use `cms_upload_asset_from_base64` (small inline base64) or `cms_upload_asset_from_url` (public HTTPS URL) instead. Flow: the asset row is created in `uploaded:false` state. Look at `uploadMethod`: if `"PUT"`, send the file body as the raw PUT body to `uploadUrl`. If `"POST"`, send multipart/form-data to `uploadUrl` including every key/value in `uploadFields` followed by a `file` part; the embedded policy enforces an exact `Content-Length` match. Then call `cms_complete_asset_upload` to verify and mark the row live. SVG uploads are rejected — SVG can carry inline scripts and is not safe to serve as an asset.
Input schema
Upload a small asset inline as base64 (≤100 KB decoded). The right choice when you have generated the asset in this conversation (image-gen output, screenshot, plot) and need it in the CMS without leaving the chat: compress to WebP or JPEG well under 100 KB first, then pass the bytes here. SVG is rejected. For larger assets reachable over HTTPS use `cms_upload_asset_from_url`; for larger arbitrary files use `cms_request_asset_upload` from a client that can issue HTTP PUT.
Input schema
Fetch a publicly reachable HTTPS asset and store it as a CMS asset in one call. Use this when your runtime cannot PUT to a presigned URL or pass large base64 payloads — typical for ChatGPT/Claude workspace agents whose sandbox blocks outbound PUTs and truncates long base64 strings. The server fetches the URL with SSRF protection, validates content-type (image/*, video/*, audio/*, application/pdf — SVG rejected) and size (≤50 MB), and creates the asset row in `uploaded:true` state. Filename and MIME are inferred from the response unless overridden. The original URL is recorded in `metadata.sourceUrl`.
Input schema
Mark a previously-requested asset upload as complete.
Input schema
Delete an asset and remove the underlying file from storage. Fails with a conflict if the asset is still referenced by any entry, as a typed asset field or inline in a body.
Input schema
List configured locales for your org. The default locale is used when an entry omits one.
Input schema
Add a locale. Code is ISO 639-1 (e.g. "en") or BCP-47 ("en-US"). The first locale is the default unless overridden.
Input schema
Set which locale is treated as the org's default for new entries.
Input schema
List entries that link to the given entry. Useful before deleting — see "what would break".
Input schema
List entries that use the given asset — as a typed asset field, as an inline reference inside a markdown/rich_text body, or inside a block. Useful before deleting an asset — an asset that is still in use cannot be deleted.
Input schema
Hybrid full-text + semantic search across CMS entries. Each hit carries a match excerpt, the derived title, and summarized field data — long text is shortened to a lead with a word count in `fieldSummary`. Returns drafts and published; the public delivery API runs the same engine but hard-filters to published-only.
Input schema
Export this org's CMS (locales, collections, entries, and assets) as a portable JSON payload. Pair with `cms_import` on another Munin server to move content between self-hosted and cloud. Asset bytes are included base64-encoded (assets larger than 5MB are exported as metadata only). Entry embeddings are not included — they are regenerated on import. Feed the returned `records` straight into `cms_import`.
Input schema
Import CMS `records` produced by `cms_export` (typically from another Munin server). Locales are upserted by code, collections by slug, entries by (collection, slug, locale), and assets by (name, size) — so re-running is idempotent. Entries that shared a `translationGroupId` on the source stay linked as locale variants here; payloads exported before translation groups existed fall back to grouping by (collection, slug). Asset bytes are re-uploaded to this server and entry references/asset ids are rewritten to local ids. Entry embeddings are regenerated here. Returns counts and an `idMap` (source id → id on this server); pass that `idMap` back into later imports so dependent records resolve their parents.
Input schema
List conversations for your org, newest activity first. Filter by status (open / snoozed / closed / spam), assignee, topic, handover state (`active` / `resolved` / `never`), or `since` an ISO timestamp. `handover: "resolved"` plus `since` is the set a knowledge-curation pass works from: questions a human answered inside the window.
Input schema
Read one conversation including every public + internal message.
Input schema
Append a message to a conversation. Pass `internal: true` to leave a staff-only note (drafts, side comments) — end-user agents never see internal messages.
Input schema
Assign a conversation to a user (pass user id) or unassign (pass null). Useful for routing escalated conversations.
Input schema
Change a conversation's status. `snoozeUntil` (ISO 8601) is required when status is "snoozed".
Input schema
Flag a conversation as needing human attention. Use this when you have reached the limit of what you can resolve autonomously — billing decisions, refunds outside policy, sensitive complaints, anything where a human teammate should step in. Appends an internal system note (visible only to staff) recording your stated `reason`, sets the conversation's "needs human attention" flag (which pins it to the top of the dashboard's Conversations page), and emits `conversation.handover_requested`. Pass `suggestedReply` ONLY when you have a substantive answer to propose — write it as the reply a teammate could send the end-user to resolve the issue, so they can edit, approve, or rewrite it. Don't fill it with a "a teammate will follow up" acknowledgement or a copy of any message already sent to the end-user; if you have no real answer to suggest, OMIT it. A suggested reply is a proposal for the next outbound message: once any public message goes out on the conversation, it is retired and the team is no longer offered it. Idempotent — calling again on an already-flagged conversation is a no-op. The flag clears automatically once a human teammate replies or closes the conversation.
Input schema
Substring search over message bodies. Returns the matching messages newest first; use conv_get_conversation to load surrounding context.
Input schema
Aggregate email open tracking per email channel over a recent window (default 30 days, max 365). Returns for each channel the number of messages delivered in the window, how many of those were opened at least once, the total open count, and the resulting open rate, plus org-wide totals. `trackOpens` reports whether the channel currently embeds the tracking pixel — a channel with tracking off records no opens, so its rate reads 0 rather than "nobody opened these". Open tracking is best-effort in general: only messages with an HTML part carry a pixel, clients that block remote images never report an open, and privacy proxies such as Apple Mail Privacy Protection pre-fetch images and inflate the count.
Input schema
List conversation channels of every kind configured for your org — email, chat (widget), SMS and voice.
Input schema
List conversation topics (Billing, Support, Refunds, …) for your org.
Input schema
Add a new conversation topic. Slug must be lowercase letters, digits, hyphens.
Input schema
Tag a conversation with one of the org's existing topics, or pass `topicId: null` to clear the topic. Use `conv_list_topics` first to see what topics exist; topics must be pre-created via `conv_create_topic`.
Input schema
Set a conversation's subject — the short human-readable title shown in the inbox and the chat widget — or pass `subject: null` to clear it. Used by the set-topic-and-title curator skill to title conversations that arrive without a subject (chat, SMS, voice). Email conversations already carry the email Subject line; don't overwrite it.
Input schema
Replace an inbound message's body with a signature-stripped version. Used by the strip-email-signature curator skill — runs after the regex quote-stripper to clean up the trailing sign-off / contact block. The original body is kept in `metadata.preStripBody` for audit; the removed signature (if provided) is stored in `metadata.signatureText`. Refuses if the new body is empty or if the message isn't an end-user inbound in the caller's org. A cut that removes more than half the body is allowed only when `signatureText` is supplied, matches the removed trailing portion, and carries multiple contact-info hints (email, phone, address, URL) — this is what lets a one-line reply followed by a large contact block be cleaned. An applied cut emits `conversation.message.body_revised`, which re-syncs the message's mirrored copy in connected operator bridges (Slack) so the signature disappears there too.
Input schema
Export this org's conversation channels, conversations, and messages as a portable JSON payload. Pair with `conv_import` on another Munin server to move conversations between self-hosted and cloud. Channel credentials are NOT included — they are encrypted with this server's key and must be re-entered on the target. Feed the returned `records` straight into `conv_import`.
Input schema
Import conversation `records` produced by `conv_export` (typically from another Munin server). Channels are upserted by (type, vendor, name) and recreated without credentials — re-enter them on this server. Conversations and messages are append-only with no natural key: fresh ids are generated and parent FKs (channelId, conversationId) are resolved through the `idMap`. Messages with `authorType: "system"` are always stored as internal staff-only notes regardless of the `internal` flag in the payload, and each coercion is reported in `warnings`. Returns counts, `warnings`, and an `idMap` (source id → id on this server); pass that `idMap` back into later imports so dependent records resolve their parents. Re-running within a single migration is idempotent via the idMap, but messages are not deduplicated across separate runs.
Input schema
Create or update an email channel's transport configuration with the non-secret fields only. SMTP / IMAP passwords are rejected here: the channel is created inactive and the response includes a one-time link for a human to enter the passwords in the dashboard — the channel activates once they are saved. Updating a channel that is currently deactivated (for example after repeated inbound polling failures) re-tests the stored credentials: the channel is reactivated when SMTP and IMAP both connect, and otherwise stays deactivated with the connection errors in the `probe` field of the response. Set `outbound.provider: 'mailer'` to send via Munin's configured Resend mailer instead of a custom SMTP host (no password needed, channel is active immediately). Set `defaultAgentMode: 'draft_only'` so the agent answers into an internal draft that a human reviews and sends, instead of replying to the sender itself — use it to run an inbox with a human in the loop, or on an outreach-only inbox where a reply must never be auto-sent.
Input schema
Test an email channel's stored credentials. Attempts an SMTP connect (and an IMAP connect if inbound is configured) without sending or fetching anything. Returns `{ smtp: "ok" | error, imap: "ok" | error | "not configured" }`.
Input schema
Send a real test email through this channel's configured outbound transport (SMTP or Mailer). The message is addressed `to` the recipient you pass in. Useful for confirming credentials and deliverability end-to-end.
Input schema
Return a one-time link a human opens to enter a channel’s secret credentials in the dashboard — secrets are never accepted in a conversation. Works for any channel kind: email (SMTP/IMAP passwords) as well as voice and SMS vendor keys. conv_configure_email_channel and conv_configure_voice_sms_channel already return this link on create; use this tool to mint a fresh link when one expired or to rotate the stored secrets. The link expires after 24 hours.
Input schema
Create a chat-widget channel and mint a widget API key (`mn_widget_*`) bound to it. Returns the plaintext key once; store it server-side and pass it as `Authorization: Bearer` when calling POST /v1/widget/messages from the external agent. Scaffolding a frontend from Lovable/Bolt/v0/Replit/Cursor? Read `skill://playbooks/frontend-integration` first — it covers the widget + tracker + CMS wiring end-to-end.
Input schema
Update a chat-widget channel's originAllowlist / webhookOnEscalation. Pass null to clear webhookOnEscalation. The widget API key is unchanged.
Input schema
Revoke any active widget keys bound to this channel and mint a fresh `mn_widget_*` key. Returns the new plaintext key once. Existing inflight requests using the old key keep working until revocation lands.
Input schema
Generate a fresh per-channel HMAC secret used to verify browser-side `data-user-hash` values against `data-external-id`. The previous secret is replaced atomically; any previously-issued user hashes stop verifying immediately and the operator must re-render their pages with newly-computed hashes. Returns the new plaintext once.
Input schema
List the voice/SMS channel vendors you can configure, with each vendor’s `kind`, capabilities (call/sendTest), and config fields (name, required, secret, description). Use this to discover what to pass to conv_configure_voice_sms_channel.
Input schema
Discover the selectable options a channel’s vendor offers using the channel’s stored credentials — e.g. Threll workers, Vapi assistants — so you can pass a valid id to conv_configure_voice_sms_channel instead of guessing. The channel must have completed its credential link first. Returns option `groups` (e.g. `workers`, `assistants`), each with `{ value, label, hint }`.
Input schema
Create or update a voice or SMS channel for any supported vendor. Pass `vendor` + the vendor’s non-secret `config` fields (see conv_list_voice_sms_vendors). Secret fields are rejected here: creating returns a pending channel plus a one-time link for a human to enter the secrets in the dashboard — the channel activates once they are saved and verified. Pass `channelId` to update; omit to create. `defaultAgentMode` applies to SMS channels only.
Input schema
Verify a voice or SMS channel’s stored credentials with its vendor (no message sent). The result shape is vendor-specific. Email channels are tested with conv_test_email_channel instead.
Input schema
Send a real test SMS through an SMS channel (Twilio, MessageBird), addressed to `to`. Useful for end-to-end deliverability checks. Voice channels have no test send — their credentials are checked with conv_test_voice_sms_channel, and a test call is placed by a human from the dashboard. Email channels use conv_send_email_channel_test.
Input schema
List the third-party systems Munin can connect to, grouped by domain (commerce, bookings, seo) with the config fields each vendor requires. Use it to see what credentials are needed before creating a connection. Vendors marked `oauth` are authorized by redirect — store their client credentials, then finish with connectors_get_authorize_url.
Input schema
List this org’s connections to third-party systems with domain, non-secret settings, active state, and the result of the last credential test. Secrets are never returned.
Input schema
Create a connection to a third-party system. `config` takes the vendor’s non-secret fields only — connectors_list_vendors returns the exact fields and marks which are secret. Secret fields are rejected here: the connection is created pending and the response includes a one-time link for a human to enter the secrets in the dashboard. The vendor determines the domain (commerce, bookings, mcp). Connection names must be unique within the org. For the `custom-mcp` vendor the connected server is a customer-facing tool source, not a toolbox for admin agents: it exposes nothing until specific tool names are listed in its `allowedTools` config.
Input schema
Return a one-time link a human opens to enter a connection’s secret credentials in the dashboard, so the secret is never pasted into a conversation. Use it for a pending connection created without its secret. The link expires after 24 hours.
Input schema
Return the vendor OAuth link that grants Munin access for a connection, for vendors that authorize by redirect instead of a pasted key (connectors_list_vendors marks which). A human must open and approve it in a browser; it expires after 10 minutes, so call again for a fresh one. Use it to finish a pending connection once its client credentials are stored, or to reconnect one whose credentialState is expired. Fails for vendors that use static credentials.
Input schema
Rename, activate/deactivate, or reconfigure a connection. When passing `config`, supply the full non-secret vendor config; the stored secret values are kept. Secret fields are rejected here — to rotate a secret, delete the connection and create it again, entering the new secret through the credential link.
Input schema
List the tools a connected custom MCP server offers, each flagged with whether it is currently exposed to customers (`allowed`) and whether the server marks it read-only (`destructive`). Use it to see what a server provides before choosing which tools customers may reach. Only applies to vendors with a selectable tool list, such as custom-mcp.
Input schema
Replace the set of tools a connected custom MCP server exposes to customers. Pass the exact tool names from connectors_list_server_tools; anything omitted stops being offered. An empty list leaves the server connected but silent. Tools reach end-users in chat, email and SMS conversations, so list only what a customer should be able to call about themselves.
Input schema
Delete a connection and its stored credentials. Lookups through this connection stop working immediately.
Input schema
Verify a connection’s stored credentials against the vendor with a read-only probe (no external data is changed). Records the result on the connection.
Input schema
List contacts in your org, newest-updated first. Filter by company or tag.
Input schema
Read one contact, including AI fields, tags, custom fields, and compliance flags.
Input schema
Find an existing contact by email and/or phone before creating a new one. Returns null if no match.
Input schema
Create a new contact. Search with crm_lookup_contact first to avoid duplicates.
Input schema
Update fields on a contact. Only keys present in `patch` are touched; omitted keys are preserved. `customFields` is a partial patch — keys you send replace the corresponding keys; keys you omit are preserved (send `key: null` to clear a single custom field). Setting `doNotContact: true` also stamps `unsubscribedAt`; setting it false clears it. Pass `mode: 'fill-null'` from automated/curator contexts to refuse overwriting existing non-null values (only null/empty fields are filled). Default `mode: 'overwrite'` applies the patch as-is and is appropriate for human-driven edits.
Input schema
Bulk-create contacts with dedupe + compliance checks: rows whose email or phone already match a do_not_contact contact are skipped, as are rows that would duplicate an existing contact.
Input schema
Substring search across name, email, phone, and title. Returns contacts ordered newest-updated first.
Input schema
List companies in your org.
Input schema
Create a new company.
Input schema
List sales pipelines for your org with their stages in position order.
Input schema
Create a new sales pipeline with at least one stage. Stages are inserted in array order; mark a stage `winLoss: "won"` or `"lost"` to record terminal outcomes.
Input schema
List deals, optionally filtered by pipeline or stage.
Input schema
Create a new deal in a pipeline. If `stageId` is omitted, the deal lands in the pipeline's first stage by position.
Input schema
Move a deal to a new stage. If the destination stage is a won/lost terminal, `closedAt` is stamped automatically.
Input schema
Record an activity (note / call / email / meeting / task) against a contact, company, or deal. If `contactId` is set, the contact's `lastContactedAt` is also bumped.
Input schema
List CRM activities filtered by contact, deal, or company.
Input schema
Set the AI-generated summary and/or next-action for a contact, company, or deal. These live in dedicated columns so agents do not pollute the human-edited description.
Input schema
File a structured proposal that two contacts are the same person. Pass `confidence` ("high" | "medium"), `evidence` (the matched signals — same email, same phone, similar name, etc.), `recommendedKeeperId` (which row to keep), and optionally `recommendedPatch` (fields to copy onto the keeper from the duplicate). Idempotent on the (contactA, contactB) pair while a pending proposal exists — calling again upserts the existing pending row. Rejects with a conflict when the pair has already been merged: either side carrying `customFields.mergedInto`, or an `applied` proposal already on record for the pair. The CRM clean-contact-data curator runs this on a periodic cadence; see `skill://crm/clean-contact-data`.
Input schema
List CRM merge proposals, defaulting to `status: "pending"` (the operator review queue). Returns each proposal with both contacts embedded as summaries — no extra `crm_get_contact` calls needed. Pass `status: "dismissed"` once per curator pass to skip pairs the operator has already rejected. In hosts that support MCP Apps this renders an interactive review panel with side-by-side contact comparison and per-proposal apply/dismiss actions.
Input schema
Atomically apply a pending merge proposal: copies `recommendedPatch` fields onto the keeper, reassigns the duplicate's activities, deals and relationships onto the keeper, archives the duplicate (adds `dedup-archived-YYYY-MM` tag, sets `customFields.mergedInto = <keeperId>`, sets `doNotContact: true`), dismisses any other pending proposals that reference the duplicate, and marks the proposal `applied`. The apply is bound to the proposal it was given: `fingerprint` must match the current `mergeFingerprint`, so a proposal whose keeper, patch or confidence changed since it was read is refused with a conflict and stays pending. Throws if the proposal is not in `pending` status.
Input schema
Mark a pending merge proposal as dismissed (the operator decided these are not the same person). The next CRM hygiene curator pass queries dismissed proposals and skips refiling the same pair. Optional `reason` is stored for audit. Throws if the proposal is not in `pending` status.
Input schema
List saved contact segments. A segment is a named CRM filter — used as the audience for outreach campaigns and other targeting workflows. Returns each segment with its filter definition.
Input schema
Read one segment, including its filter definition.
Input schema
Create a saved contact segment. `filter` supports tagsAny (any-of match), tagsAll (all-of match), companyId, searchQuery (substring over name/email/title), and contactedSince (ISO-8601 — narrows to contacts NOT contacted since that timestamp). Combine fields and they AND together.
Input schema
Patch a segment: rename, edit description, or replace the filter.
Input schema
Delete a segment. Outreach campaigns referencing it will fail until reassigned.
Input schema
Resolve a segment to its current contacts. ALWAYS excludes suppressed contacts (do_not_contact OR unsubscribed) AND contacts without a recorded lawful basis (consent_lawful_basis IS NULL). Use this — not crm_list_contacts — to materialize an outreach audience: the suppression and consent floors are non-overridable here.
Input schema
Record the lawful basis and source for contacting this person. Required before they can appear in any outreach segment. `lawfulBasis` is one of `consent`, `legitimate_interest`, or `contract`. `source` is a short label (e.g. "imported-2026-q2", "web-form-trial-signup", "event-attendee-summit-2025"). Logs a CRM activity for audit.
Input schema
Export this org's CRM (pipelines and stages, segments, companies, contacts, deals, activities, and relationships) as a portable JSON payload. Pair with `crm_import` on another Munin server to move a CRM between self-hosted and cloud. Feed the returned `records` straight into `crm_import`.
Input schema
Import CRM `records` produced by `crm_export` (typically from another Munin server). Records are imported in dependency order (pipelines → segments → companies → contacts → deals → activities → relationships). Pipelines are upserted by slug, segments by name, companies by domain (else name), and contacts by email — so re-running is idempotent. Deals, activities, and relationships get fresh ids each run with their parents resolved through the `idMap`. Returns counts and an `idMap` (source id → id on this server); pass that `idMap` back into later imports so dependent records resolve their parents.
Input schema
List knowledge-base spaces in your org.
Input schema
Create a new knowledge-base space. Slug must be unique within your org and only contain lowercase letters, digits and hyphens.
Input schema
List knowledge-base documents in your org, newest-updated first, with each document's `sourceUrl` (the public page it came from, or null). Bodies are not included; read one with `kb_get_document`. Optionally filter by space or tag.
Input schema
Read one knowledge-base document, including its full body, tags, `sourceUrl` (the public page it came from, or null), and current version. End-user agents see only documents whose `audiences` includes `'self_service'`.
Input schema
Read a knowledge-base document by its space slug and document slug — used when a stable identifier (e.g. 'agent-runtime/system-prompt') is needed instead of the document UUID. Returns null when the document does not exist.
Input schema
Search the knowledge base by natural-language query. Combines full-text search and vector similarity for the best of both. Each hit carries the document id, title, a matching excerpt, and `sourceUrl` — the public page the document came from, when it has one — so an answer can link to the source without a second read. End-user agents see only documents whose `audiences` includes `'self_service'`.
Input schema
Create a knowledge-base document inside a space. Body should be markdown. Set `audiences: ['admin', 'self_service']` to expose it to end-user agents; defaults to `['admin']` (admin-only). Set `sourceUrl` when the document mirrors a public page — a help-centre article, a product page — and answers drawn from it can link there.
Input schema
Export this org's knowledge base (spaces and non-system documents) as a portable JSON payload. Pair with `kb_import` on another Munin server to move a knowledge base between self-hosted and cloud. Embeddings are not included — they are regenerated on import. Feed the returned `records` straight into `kb_import`.
Input schema
Import knowledge-base `records` produced by `kb_export` (typically from another Munin server). Spaces are upserted by slug and documents by (space, slug) — or (space, title) when a document has no slug — so re-running is idempotent. Embeddings are regenerated here. Returns counts and an `idMap` (source id → id on this server); pass that `idMap` back into later imports so dependent records resolve their parents.
Input schema
Crawl a public website and populate the knowledge base from it: one KB document per page. Runs asynchronously on the curator queue — returns a job id you can track with the curator jobs control plane. Pass a homepage URL (a bare domain like `example.com` is accepted). The URL must be publicly reachable; localhost and private/internal addresses are rejected. Re-importing the same URL while a scrape is still pending returns the in-flight job instead of starting a second one. By default the import also synthesizes a `company-profile` KB document (slug `company-profile`) that seeds the chat widget — appropriate when importing your own company's site. Set `synthesizeCompanyProfile: false` when importing third-party or topic pages that are NOT your company's website, so the import doesn't overwrite your company profile with unrelated content. Reconciliation is on by default: after a healthy crawl, previously imported pages that are no longer on the site (re-checked individually and confirmed to return 404/410) are deleted from the knowledge base, so a refresh prunes removed pages. Set `reconcile: false` to import additively without pruning.
Input schema
Check the progress of a website import started with `kb_import_website`, by the job id it returned. Status is one of `pending` (queued or running), `done` (finished — `summary` reports how many documents were imported), `failed_retryable`/`dead` (will retry / gave up), or `failed`. While `pending`, poll again after a short delay.
Input schema
Update a knowledge-base document. Pass `ifVersion` (the current version you read) for optimistic concurrency; the call fails if it has changed. Omitted fields keep their current value; `sourceUrl: null` clears the recorded source page.
Input schema
Delete a knowledge-base document. Pass `ifVersion` for optimistic concurrency. Cascades to chunks and versions. System-managed docs (e.g. the seeded `agent-runtime` prompts) cannot be deleted — edit their content with `kb_update_document` instead.
Input schema
List all prior versions of a knowledge-base document, newest first.
Input schema
File a draft FAQ-style document into the `kb-curation-inbox` KB space (admin audience only), for knowledge the KB does not cover at all. Used after a curation pass over resolved-handover conversations. The space is created on first use. See `skill://kb/review-content` for the procedure. To correct or extend a document that already exists, file `kb_propose_curation_revision` instead — it publishes as a new version of that document rather than a second one beside it. The candidate is NOT visible to end-user agents until it's promoted with `kb_publish_curation_candidate`. Fails with `kb_curation_decided` when a candidate from the same source message was already dismissed or published; a decision recorded before per-message curation shipped closes its whole conversation.
Input schema
File a proposed new body for a document that already exists, into the `kb-curation-inbox` KB space (admin audience only). Use it when what you learned corrects, contradicts or extends a document rather than filling a gap — a changed rate, a superseded policy, a missing exception. Pass the full proposed body, not a patch; the reviewer sees it as a diff against the document's current text. Publishing it with `kb_publish_curation_revision` writes a new version of that same document, so `kb_list_versions` and `kb_restore_version` can roll it back. Fails with `kb_curation_decided` when a candidate from the same source message was already dismissed or published.
Input schema
List pending curation candidates awaiting review — drafts filed into the `kb-curation-inbox` space. Each row carries the source conversation id parsed from its tags, plus either a proposed target space slug (a new document) or `revisesDocumentId` with the revised document's current title and version (a proposed new version of an existing document). Bodies are not included; read one with `kb_get_document`. In hosts that support MCP Apps this renders an interactive review panel with per-candidate publish/dismiss actions.
Input schema
Reject a curation candidate: deletes the draft from the `kb-curation-inbox` space and records the decision, with an optional reason. The decision is permanent and scoped to the source conversation — later curation passes are refused when they try to file another candidate from it, so a rejected draft stays rejected instead of reappearing on the next weekly sweep. Pass `ifVersion` (the candidate `version` that was reviewed). Read past decisions with `kb_list_curation_decisions`.
Input schema
List curation candidates that were already decided — dismissed (with the reason, when one was given) or published — newest first. Filter by `outcome` or `sourceConversationId`. A conversation that appears here is closed for curation: `kb_propose_curation_candidate` refuses further candidates from it.
Input schema
Promote a reviewed curation candidate into a target KB space as a new document. Copies the doc to the target space (default audiences `['admin', 'self_service']` so the self-service agent can find it), drops the curation tags, and removes the candidate from the inbox. The target space is created from the slug if it does not exist yet. Pass `ifVersion` (the candidate `version` that was reviewed) for optimistic concurrency; if the draft was edited since, the call fails and nothing is published. Refuses a candidate that proposes a revision to an existing document — use `kb_publish_curation_revision` for those.
Input schema
Apply a reviewed revision candidate to the document it revises, as a new version of that document, then remove the candidate from the inbox. Takes two versions: `ifCandidateVersion` binds the publish to the proposed text that was reviewed, and `ifDocumentVersion` binds it to the document text that was diffed against — if either moved since, the call fails and nothing is written. Roll back with `kb_restore_version`.
Input schema
Roll a document back to an earlier version. Creates a new current version with that historical content.
Input schema
List outbound-campaign definitions for this org. Each row carries the brief, the targeted CRM segment, the email channel used to send, cadence rules, `sequenceSteps` (ordered follow-up steps drafted by the daily curator when the campaign is enabled; empty means no sequence), CTA URL, the enabled flag, and the automation flags: `autoDraftFirstTouch` (the weekly curator drafts first-touch emails only when true), `autoDraftReplies` (replies to inbound prospect messages are auto-drafted only when true), and `autoCurateEdits` (a human editing a draft before approving it feeds a KB curation pass only when true). The weekly first-touch curator only drafts proposals for `enabled = true` campaigns with `autoDraftFirstTouch = true`.
Input schema
Read a single campaign by id, including brief and cadence rules.
Input schema
Create an outbound-campaign definition. Operators write `brief` as a one-paragraph human description of intent (the curator personalises per contact from this). `segmentId` chooses the audience; the curator calls `crm_list_contacts_in_segment` (which always enforces suppression+consent floor) to materialize it. `channelId` must reference an email, SMS, or voice channel; approving a proposal on an SMS or voice campaign is restricted to a signed-in person in the Munin dashboard. New campaigns default `enabled: false` so nothing sends until you flip it on. Automation is opt-in per behavior: `autoDraftFirstTouch` defaults false (the weekly curator does not draft first-touch emails until you set it true — draft manually otherwise), `autoDraftReplies` defaults true (replies to inbound prospect messages are auto-drafted for review), and `autoCurateEdits` defaults false (when true, a proposal a human edited before approving queues a KB curation pass over what they changed — leave it off unless edits on this campaign tend to correct facts rather than personalise copy). Auto-sending a reply is not an option on any campaign: conversations created by an approved proposal are always set to `draft_only`, whatever the channel default says, so a prospect never receives an unreviewed reply. Optional `sequenceSteps` (email campaigns only) defines a follow-up sequence — each step is a wait period plus a drafting brief; defining steps on an enabled campaign opts it into daily follow-up drafting for threads with no reply.
Input schema
Export this org's outbound campaigns and their queued proposals as a portable JSON payload. Pair with `outreach_import` on another Munin server. Campaigns reference a CRM segment and a conversation channel, and proposals reference CRM contacts/conversations — so export and import CRM and Conversations first, and thread their `idMap` into `outreach_import`.
Input schema
Import outreach `records` produced by `outreach_export`. Campaigns are upserted by name and proposals by (campaign, contact, kind) — plus `sequenceStep` for follow-ups — so re-running is idempotent. Segment, channel, contact and conversation foreign keys are resolved through the supplied `idMap` (pass the idMap returned by the CRM and Conversations imports). Campaigns are imported **disabled** — re-enable them after re-entering the channel credentials. Proposals that were approved and awaiting a scheduled send on the source server arrive as `pending` with a warning, so no timer follows the data across servers. Returns counts plus the merged `idMap`.
Input schema
Patch fields on a campaign — rename, swap segment, adjust cadence, toggle enabled, toggle the automation flags `autoDraftFirstTouch` (weekly first-touch drafting), `autoDraftReplies` (auto-drafting replies to inbound prospect messages) and `autoCurateEdits` (KB curation over what a human changed before approving a draft), or replace `sequenceSteps` (the follow-up sequence; pass the full array, email campaigns only, empty array removes the sequence).
Input schema
List drafted outreach proposals, newest first. Defaults to all statuses and to 25 rows; pass `status` and `limit` to narrow. Rows carry the full `draftSubject` / `draftBody` plus nested `contact`, `campaign` and `delivery` summaries, but not the curator `evidence` payload, which can run to thousands of characters per row — a boolean `hasEvidence` says whether there is any, and `outreach_get_proposal` returns it for one proposal. The first-touch curator queries `status: "pending", kind: "initial"` filtered by `(campaignId, contactId)` to dedupe before drafting a new candidate. The operator review surface queries `status: "pending"`. `status: "approved"` lists approved sends still waiting for their `scheduledSendAt`, soonest first. In hosts that support MCP Apps this renders an interactive review panel with per-proposal approve/dismiss actions.
Input schema
Read one outreach proposal by id, including the full curator `evidence` payload that `outreach_list_proposals` omits — the sources, compliance notes and reasoning recorded when the draft was filed. Also carries the draft, its status and decision history, and the nested `contact`, `campaign` and `delivery` summaries.
Input schema
Approve one pending outreach proposal, authorizing it to go out: an initial proposal creates the outbound conversation and delivers the first touch on the campaign's channel — an email (with CTA and unsubscribe footer per campaign settings), an SMS, or an outbound call placed through the channel's voice vendor; a reply or follow-up proposal sends the draft verbatim on its existing conversation. Sends immediately unless a future send time applies, in which case the proposal returns `status: "approved"` with `scheduledSendAt` and a background worker delivers it then, re-checking campaign state, suppression and quiet hours at that moment. The approval is bound to the draft it was given: `fingerprint` must match the proposal's current `draftFingerprint`, so a draft revised since it was read is refused with a conflict and stays pending. Also fails if the proposal is not pending, if the campaign is disabled, if the contact became suppressed since drafting, or — for follow-ups — if the prospect replied after the draft was filed (dismiss it; the reply flow takes over). Returns the proposal with `status: "sent"`, `conversationId` and `sentMessageId` on an immediate send.
Input schema
Call off a scheduled outreach send before the worker delivers it, returning the proposal to `status: "pending"` so it goes back on the review queue with its draft and revision history intact. The approval is cleared, so sending it later takes a fresh approval. Nothing is sent and the contact is not suppressed. Fails when the proposal is not an approved, still-scheduled send — a proposal already delivered cannot be recalled. Takes a required `reason`.
Input schema
Dismiss one pending outreach proposal without sending, optionally recording a reason. The decision (actor and timestamp) is kept on the proposal for audit. Fails if the proposal is not pending — an approved send waiting on its schedule has to be called off with `outreach_cancel_scheduled_send` first, which puts it back in `pending`. Returns the proposal with `status: "dismissed"`.
Input schema
Rewrite the draft on one pending outreach proposal in place, keeping the same proposal id, campaign, and contact — those three cannot be changed here; a different recipient or campaign is a different proposal. Pass any of `draftSubject`, `draftBody`, `proposedSendAt` plus a required `reason`. The revision is recorded on the proposal: `revisionCount`, `lastRevisedAt`, `lastRevisionReason`, and the revising actor, plus `revisedAfterReviewAt` when someone else had already opened the draft for review before the change. `proposedSendAt` is the send time an operator inherits when they approve without naming one of their own. Fails if the proposal is not pending — an approved send waiting on its schedule has to be called off with `outreach_cancel_scheduled_send` before its draft can change. Returns the revised proposal.
Input schema
Retract one pending outreach proposal that should no longer be reviewed — a duplicate draft, a prospect who turned out not to qualify, a bounced address. Nothing is sent. This is a neutral retraction, not a rejection: it does not suppress the contact, does not change their consent, and does not stop a campaign sequence (a withdrawn follow-up leaves later steps eligible, unlike a dismissed one). A required `reason` and the withdrawing actor are recorded. Fails if the proposal is not pending. Returns the proposal with `status: "withdrawn"`.
Input schema
File one first-touch outreach draft per (campaign, contact) for human approval — an email body, an SMS body, or the script for an outbound call, depending on the channel the campaign sends on. Idempotent: re-proposing the same (campaign, contact, kind=initial) throws when a pending draft already exists, or when the contact already has a sent or approved first-touch in this campaign (they were already reached) — call `outreach_list_proposals` first to dedupe. Suppression and consent are re-checked at approve-time too; this tool refuses up-front if the contact is already suppressed.
Input schema
File a drafted reply to an inbound message on an outreach-originated conversation, for human approval. The conversation must have an `outreachCampaignId` set (it's an outreach conversation) and a CRM contact resolvable by email. Idempotent: re-proposing while a pending reply exists for the same conversation throws — the operator should approve or dismiss the existing one first. Reply approvals send via `conv_send_message` on the existing conversation; no unsubscribe footer is appended (replies thread inside the existing email chain that already carries the unsubscribe link).
Input schema
List outreach conversations whose next sequence step is due now. A row is returned when the campaign is enabled with a `sequenceSteps` entry beyond the last sent outbound, the wait period has elapsed with zero inbound replies, the conversation is open and unassigned, the contact is not suppressed, and no pending follow-up/reply or dismissed step blocks it. Campaign cadence rules are also honored: a contact at their `maxPerWeekPerContact` budget of sent touches in the trailing 7 days is held back, and nothing is due on a `blackoutDates` day (quiet hours are a send-time concern and do not gate drafting). Each row carries `conversationId`, `nextStep`, and the step brief — everything needed to draft and file the follow-up via `outreach_propose_followup`. An empty result means no sequence work is due.
Input schema
File a drafted sequence follow-up (step N of the campaign's `sequenceSteps`) on an outreach conversation, for human approval. Follow-up sequences run on email campaigns only. `step` must be the next step for the conversation, its wait period must have elapsed, and the prospect must not have replied — any inbound reply permanently stops the sequence (the reply flow owns the conversation). One pending follow-up per (campaign, contact); a dismissed follow-up permanently stops the sequence for that contact, so operators who dislike the wording should edit-then-approve instead. Approving sends on the existing conversation with no subject or unsubscribe footer (the thread already carries both).
Input schema
List all webhook subscriptions for your org. Secrets are not returned — they are only shown once at creation. Use webhooks_rotate_secret if you lost it.
Input schema
Create a webhook subscription. `url` must be https://. `events` is an array of event type strings (e.g. ["cms.entry.published"]); omit or pass an empty array to subscribe to all events. The response includes a one-time `secret` of the form `whsec_…` — store it now; it cannot be retrieved later. Deliveries are signed with `x-munin-signature: sha256=<HMAC-SHA256(body, secret)>` and include `x-munin-event`, `x-munin-delivery-id` headers. The signed body carries `createdAt` — reject deliveries whose `createdAt` is outside your freshness window, and dedupe on `x-munin-delivery-id`, to defend against replay. Retries: up to 5 attempts with exponential backoff (30s → 8m). Use webhooks_list_event_types to discover known event names.
Input schema
Patch a webhook subscription. Pass only the fields you want to change. Replacing `events` overwrites the full array — read first if you mean to append.
Input schema
Delete a webhook. Pending deliveries are cascade-deleted; in-flight HTTP attempts finish their current run.
Input schema
Generate a new `whsec_…` secret for a webhook. The previous secret stops signing deliveries immediately — update your receiver before rotating. The new secret is returned once in the response.
Input schema
List delivery attempts for one webhook, newest first. Each row has attempt count, statusCode, durationMs, error, deliveredAt, nextAttemptAt, and a rolled-up `status` (pending / delivered / failed). Filter with `status` and cap with `limit` (default 50, max 200).
Input schema
List the known event type strings emitted across modules (cms, crm, kb, conv, outreach, system). Use the returned strings as input to webhooks_create. The subscription accepts arbitrary strings, so future event types work without a tool update — but this is the canonical catalog today.
Input schema
Return the Slack OAuth link that connects a Slack workspace to this org. The link must be opened and approved in a browser by a workspace admin; it expires after 10 minutes (call again for a fresh one). After installing, pick a channel with slack_set_routing. Fails when the deployment has no Slack app configured (SLACK_CLIENT_ID / SLACK_CLIENT_SECRET).
Input schema
Show the org's Slack connection: whether the deployment has a Slack app configured, the connected workspace, channel routing, and mirror-delivery counts (pending + failed in the last 24h). The bot token is never returned.
Input schema
List the public channels in the org's connected Slack workspace (id, #name, and whether the bot is already a member). Use to pick a channel ID for slack_set_routing without asking the operator to look it up in Slack. Archived and private channels are not included.
Input schema
Point conversation mirroring at a Slack channel. purpose 'default' receives every conversation as a thread; purpose 'escalations' receives handover alerts (optionally with a mention); purpose 'approvals' receives approval-queue notifications (CRM merge proposals, outreach drafts, KB curation candidates) with approve/dismiss buttons; purpose 'content' receives an announcement whenever a CMS entry is published, linking the live article when the collection has a liveUrl template; convChannelId scopes a route to one source conversation channel (e.g. widget → #support-chat, email → #support-email). Calling again with the same purpose or convChannelId replaces that route. Every route needs its own Slack channel, and a Slack channel can only serve one Munin org. The response includes botInChannel — when false, invite the bot in Slack (/invite) before messages can post.
Input schema
Post a test message to the configured default Slack channel to verify the connection end-to-end. Fails with a specific error when the workspace is not connected, no default route is set, or the bot has not been invited to the channel.
Input schema
List the Slack-user ↔ Munin-member links used to attribute thread replies and button clicks. Links are created automatically by profile-email match on first use, or manually with slack_link_user.
Input schema
Manually link a Slack user (U… member ID) to a Munin org member so their thread replies and button clicks are attributed to that member. Use when the Slack profile email does not match the member email. Linking again replaces the previous mapping for that Slack user.
Input schema
Remove a Slack-user ↔ Munin-member link. Their next thread reply or button click is rejected until re-linked (manually or by email auto-match).
Input schema
Disconnect the org's Slack workspace. Deletes the stored bot token, channel routing, and all conversation/message thread links; existing Slack messages remain in Slack. Conversations in Munin are unaffected. Reconnect any time with slack_get_install_url.
Input schema
List operational alerts for the org. Defaults to open alerts only; pass includeResolved to see history.
Input schema
Read a single alert by id.
Input schema
Mark an alert as acknowledged. Does not resolve it; only signals that someone is on it.
Input schema
Manually resolve the open alert for the given source+subject. Writers normally self-resolve; use this when the underlying state can no longer be observed.
Input schema
List a customer’s recent store orders by email (newest first), e.g. while handling their support conversation. `connectionId` is only needed when multiple commerce connections are active.
Input schema
Fetch one order for a customer email, including line items and shipment tracking. Identify the order by `orderRef` (from an order listing) or the human-facing `orderNumber` the customer knows. Returns not-found unless the order belongs to that email.
Input schema
Search the connected store’s live product catalog by name or SKU. Returns published products with price range, currency, image, and storefront link. Only published/enabled products are visible. `connectionId` is only needed when multiple commerce connections are active. In hosts that support MCP Apps this renders an inline product gallery.
Input schema
Fetch one published product from the connected store, including description and per-variant price and availability (`availableForSale`; null when the store doesn’t expose stock). Identify it by `productRef` (from a catalog search) or `sku`. Returns not-found for unpublished products.
Input schema
List a guest’s bookings by email (most recent first), e.g. while handling their support conversation. `connectionId` is only needed when multiple booking connections are active.
Input schema
Fetch one booking for a guest email, including party size, start time, and duration. Identify it by `bookingRef` from a listing, or `confirmationCode` where the venue’s system issues one. Returns not-found unless the booking belongs to that email.
Input schema
List open time slots for a date and party size at the connected booking system, so you can offer real times before creating a booking. `connectionId` is only needed when multiple booking connections are active.
Input schema
Create a booking in the connected booking system for a guest email at a date and time, for a given party size. Check bookings_check_availability first. Returns the new bookingRef.
Input schema
Change an existing booking (date, time, party size, or note) by `bookingRef`. Only the fields you pass are changed.
Input schema
Cancel an existing booking by `bookingRef` in the connected booking system. This cannot be undone.
Input schema
List the verified sites the connected search-engine account can report on. Use it to discover the `siteUrl` values the other seo_* tools accept. `connectionId` is only needed when multiple seo connections are active.
Input schema
List the search queries a property was impressed and clicked for, aggregated over a date window and sorted by impressions. Each row carries impressions, clicks, ctr and avgPosition. Search-engine reporting lags 2–3 days and Bing reports in whole weeks, so the returned `window` is the range actually covered and can be narrower than the `from`/`to` requested; it is null when no data fell in range. Defaults to the last 90 days. `siteUrl` is only needed when the account has multiple verified properties.
Input schema
List the property’s pages by search impressions over a date window, each with impressions, clicks, ctr and avgPosition. Same reporting caveats as seo_list_queries: results lag 2–3 days, Bing aggregates by week, and the returned `window` is the range actually covered. Defaults to the last 90 days.
Input schema
Fetch what the search engine knows about one URL on a verified property: whether it is indexed, plus whichever of `detail` (the engine’s own coverage state), `httpStatus`, `lastCrawledAt`, `discoveredAt` and `inboundAnchorCount` that engine exposes — the set differs by engine, and a null field means it is not reported rather than zero. Returns not-found when the engine holds no record for the URL.
Input schema
Submit up to 500 URLs on a verified property to the search engine for (re)crawling, e.g. after publishing or updating a page. URLs must be under the property. Returns how many were submitted plus the remaining daily and monthly quota; the call is rejected up front when the batch exceeds the quota left, rather than partially submitting. Submissions count against a per-site daily cap, so submit only URLs whose content actually changed. Not every engine offers URL submission — Bing does, Google Search Console does not, and the call fails with a clear message for engines that don’t.
Input schema
Submit feedback about Munin. Stays local until an org admin approves it; dismissal deletes the item. Set includeOrgName / includeUserName to attach attribution; both default false.
Input schema
List feedback items in the local outbox awaiting admin action.
Input schema
Read a single feedback item by id.
Input schema
Approve a feedback item: transmits the contents to Munin developers and deletes the local row on success. Attribution is included only when the submitter opted in.
Input schema
Dismiss (delete) a pending feedback item. Nothing is sent to Munin.
Input schema
Search the public Munin roadmap for items matching a query. Call this before feedback_create_item to find an existing item to vote on instead of filing a duplicate. Returns only items the Munin team has published; pending and rejected items are hidden. sort defaults to votes (highest first); use "recent" for newest. limit is capped at 100.
Input schema
Cast this instance's vote on a published roadmap item, optionally attaching a short comment. Idempotent: a second call from the same instance returns { alreadyVoted: true } without inflating the count. Throws feedback_item_not_found if the id is unknown or the item is not public, and feedback_vote_quota_exceeded if the per-instance quota has been hit.
Input schema
Self-service tools 18
Visible to delegated end-user tokens. Scoped to one principal and read-only or contributor at most.
Verify the MCP pipe; echoes a message and returns the resolved org id, org name and actor type.
Input schema
Flag the current conversation as needing human attention. Use this when you can't answer the end-user's question on your own — pricing exceptions, account-specific issues you can't verify, anything sensitive. Pass the exact `conversationId` you were given in the system context. Sets a "needs human attention" flag on the conversation (pinning it to the top of the team's dashboard) and posts an internal note recording your `reason`. Pass `suggestedReply` ONLY when you actually have a substantive answer to propose — write it as the reply you would send the end-user to resolve their question, so a teammate can review, edit, and send it. It must NOT be the "a teammate will follow up" acknowledgement, and must NOT repeat the message you are sending the end-user this turn. If you are escalating precisely because you don't have an answer, OMIT `suggestedReply` entirely — an empty draft is far better than one that just parrots your deferral. A suggested reply is a proposal for the next outbound message: once any public message goes out on the conversation, it is retired and the team is no longer offered it. After calling this, do not keep generating replies on your own — let the user know a teammate will follow up, then stop. The flag clears once a teammate replies. The end-user does not see the system note or the suggested reply — only the team does.
Input schema
Place an outbound phone call to the contact in this conversation. Use this only when the user has asked to be called — e.g. "can you call me?". The call goes to the phone number already on file for this conversation's contact; you cannot specify an arbitrary number. The org must have an active Vapi voice channel configured. After requesting the call, tell the user briefly that a call is on the way and stop replying — the rest of the conversation happens on the phone.
Input schema
Read the CRM contact linked to the calling end-user. RLS restricts visibility to that single row.
Input schema
Update the calling end-user's own contact record. Only basic personal fields (name, phone, address) are editable from this surface — tags, ownership, custom fields, and AI fields are admin-only.
Input schema
Record an activity attributed to the calling end-user agent (e.g. a voice agent logging "spoke with customer for 4m, follow-up needed"). Auto-scoped to the end-user's own CRM contact when one exists.
Input schema
Read one knowledge-base document, including its full body, tags, `sourceUrl` (the public page it came from, or null), and current version. End-user agents see only documents whose `audiences` includes `'self_service'`.
Input schema
Search the knowledge base by natural-language query. Combines full-text search and vector similarity for the best of both. Each hit carries the document id, title, a matching excerpt, and `sourceUrl` — the public page the document came from, when it has one — so an answer can link to the source without a second read. End-user agents see only documents whose `audiences` includes `'self_service'`.
Input schema
Search the connected store’s live product catalog by name or SKU. Returns published products with price range, currency, image, and storefront link. Only published/enabled products are visible. `connectionId` is only needed when multiple commerce connections are active. In hosts that support MCP Apps this renders an inline product gallery.
Input schema
Fetch one published product from the connected store, including description and per-variant price and availability (`availableForSale`; null when the store doesn’t expose stock). Identify it by `productRef` (from a catalog search) or `sku`. Returns not-found for unpublished products.
Input schema
List the calling end-user’s recent store orders (newest first). Scoped server-side to the email on the end-user’s own record — other customers’ orders are never visible. `connectionId` is only needed when the org has multiple active store connections.
Input schema
Fetch one of the calling end-user’s orders, including line items and shipment tracking. Identify it by `orderRef` (from an order listing) or the human-facing `orderNumber` on the order confirmation. Returns not-found unless the order belongs to the calling end-user.
Input schema
List open time slots for a date and party size at the connected booking system, so you can offer real times before creating a booking. `connectionId` is only needed when multiple booking connections are active.
Input schema
List the calling end-user’s restaurant/venue bookings (most recent first). Scoped server-side to the email on the end-user’s own record — other guests’ bookings are never visible. `connectionId` is only needed when the org has multiple active booking connections.
Input schema
Fetch one of the calling end-user’s bookings, including party size, start time, and duration. Identify it by `bookingRef` from a listing, or `confirmationCode` where the venue’s system issues one. Returns not-found unless the booking belongs to the calling end-user.
Input schema
Create a booking for the calling end-user at a date and time for a party size. The booking is made under the end-user’s own email — you cannot book on behalf of anyone else. Check bookings_check_availability first.
Input schema
Change one of the calling end-user’s own bookings (date, time, party size, or note) by `bookingRef`. Returns not-found unless the booking belongs to the calling end-user.
Input schema
Cancel one of the calling end-user’s own bookings by `bookingRef`. Returns not-found unless the booking belongs to the calling end-user. This cannot be undone.