MCP Reference
Quackback's MCP server exposes 40 tools and 6 resources. Tools perform actions (search, create, update, delete). Resources provide lookup data (boards, statuses, tags) that tools reference by ID.
Tools
search
Search feedback posts or changelog entries. Returns paginated results with a cursor for the next page.
| Parameter | Type | Default | Description |
|---|---|---|---|
entity | "posts" | "changelogs" | "articles" | "posts" | Entity type to search |
query | string | - | Text search across titles and content |
boardId | string | - | Filter posts by board TypeID (ignored for changelogs) |
status | string | - | Filter by status slug. Posts: "open", "in_progress", etc. Changelogs: "draft", "published", "scheduled", "all" |
tagIds | string[] | - | Filter posts by tag TypeIDs (ignored for changelogs) |
sort | "newest" | "oldest" | "votes" | "newest" | Sort order. "votes" only applies to posts |
showDeleted | boolean | false | Show soft-deleted posts (team only, last 30 days) |
dateFrom | string | - | ISO 8601 date. Filter posts created on or after this date |
dateTo | string | - | ISO 8601 date. Filter posts created on or before this date |
limit | number (1-100) | 20 | Max results per page |
cursor | string | - | Pagination cursor from a previous response |
Scope: read:feedback for posts and changelogs; read:article when entity is "articles"
search({ query: "dark mode", sort: "votes", limit: 10 })
search({ entity: "changelogs", status: "published" })
search({ entity: "articles", query: "getting started", status: "published" })
search({ boardId: "board_01abc...", status: "open" })
search({ dateFrom: "2026-01-01", dateTo: "2026-02-01" })
search({ showDeleted: true })
get_details
Get full details for any entity by TypeID. The entity type is auto-detected from the ID prefix. No need to specify whether it's a post or changelog.
| Parameter | Type | Description |
|---|---|---|
id | string | TypeID of the entity (e.g., post_01abc..., changelog_01xyz...) |
Scope: read:feedback for posts and changelogs; read:article for article_* / kb_article_* / kb_category_* IDs
Returns all fields including comments (for posts), linked posts (for changelogs), tags, official response, and timestamps.
get_details({ id: "post_01abc..." })
get_details({ id: "changelog_01xyz..." })
get_details({ id: "article_01abc..." })
get_details({ id: "kb_category_01abc..." })
triage_post
Update a post's status, tags, or owner. All fields except postId are optional. Only provided fields are updated.
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID to update |
statusId | string | New status TypeID |
tagIds | string[] | Replace all tags with these TypeIDs |
ownerPrincipalId | string | null | Assign to a team member TypeID, or null to unassign |
Scope: write:feedback + team role
triage_post({ postId: "post_01abc...", statusId: "status_01xyz..." })
triage_post({ postId: "post_01abc...", ownerPrincipalId: "principal_01xyz..." })
triage_post({ postId: "post_01abc...", tagIds: ["tag_01a...", "tag_01b..."] })
vote_post
Toggle a vote on a feedback post. Call once to vote, call again to unvote.
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID to vote on |
Scope: write:feedback
Returns the current vote state and total vote count.
vote_post({ postId: "post_01abc..." })
proxy_vote
Add or remove a vote on behalf of another user. Use this to record votes from external channels (support tickets, Slack messages, sales calls) with source attribution for traceability.
| Parameter | Type | Default | Description |
|---|---|---|---|
action | "add" | "remove" | "add" | Whether to add or remove the proxy vote |
postId | string | - | Post TypeID to vote on |
voterPrincipalId | string | - | Principal TypeID of the user to vote on behalf of |
sourceType | string | - | Attribution source type (e.g. "zendesk", "slack", "intercom") |
sourceExternalUrl | string | - | URL linking to the originating record |
Scope: write:feedback + team role
Adding a vote is idempotent: calling it twice for the same user has no effect. Removing a vote works for any vote type (proxy, integration, or direct).
proxy_vote({ postId: "post_01abc...", voterPrincipalId: "principal_01xyz..." })
proxy_vote({
postId: "post_01abc...",
voterPrincipalId: "principal_01xyz...",
sourceType: "zendesk",
sourceExternalUrl: "https://support.example.com/tickets/1234"
})
proxy_vote({ action: "remove", postId: "post_01abc...", voterPrincipalId: "principal_01xyz..." })
add_comment
Post a comment on a feedback post. Supports threaded replies via parentId.
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID to comment on |
content | string | Comment text (max 5,000 characters) |
parentId | string | Parent comment TypeID for a threaded reply |
Scope: write:feedback
add_comment({ postId: "post_01abc...", content: "Thanks for the feedback!" })
add_comment({
postId: "post_01abc...",
content: "Good point.",
parentId: "comment_01xyz..."
})
update_comment
Edit a comment's content. Team members can edit any comment. Authors can edit their own.
| Parameter | Type | Description |
|---|---|---|
commentId | string | Comment TypeID to edit |
content | string | New comment text (max 5,000 characters) |
Scope: write:feedback
update_comment({ commentId: "comment_01abc...", content: "Updated response." })
delete_comment
Hard-delete a comment and all its replies. This cannot be undone. Authors can delete their own comments. Team members can delete any comment.
| Parameter | Type | Description |
|---|---|---|
commentId | string | Comment TypeID to delete |
Scope: write:feedback
Deleting a comment also deletes all replies. This action is permanent.
delete_comment({ commentId: "comment_01abc..." })
react_to_comment
Add or remove an emoji reaction on a comment.
| Parameter | Type | Description |
|---|---|---|
action | "add" | "remove" | Whether to add or remove the reaction |
commentId | string | Comment TypeID to react to |
emoji | string | Emoji character (e.g., "👍", "❤️", "🎉") |
Scope: write:feedback
react_to_comment({ action: "add", commentId: "comment_01abc...", emoji: "👍" })
react_to_comment({ action: "remove", commentId: "comment_01abc...", emoji: "👍" })
create_post
Submit new feedback on a board. Requires a board ID and title. Content, status, and tags are optional.
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board TypeID (use quackback://boards resource to find IDs) |
title | string | Post title (max 200 characters) |
content | string | Post body (max 10,000 characters) |
statusId | string | Initial status TypeID (defaults to board default) |
tagIds | string[] | Tag TypeIDs to apply |
Scope: write:feedback
Use the quackback://boards resource to look up board IDs before calling create_post.
create_post({ boardId: "board_01abc...", title: "Add dark mode" })
create_post({
boardId: "board_01abc...",
title: "Add dark mode",
content: "Would love a dark theme option.",
statusId: "status_01xyz...",
tagIds: ["tag_01a..."]
})
delete_post
Soft-delete a feedback post. The post is hidden from public views but can be restored within 30 days.
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID to delete |
Scope: write:feedback + team role
delete_post({ postId: "post_01abc..." })
restore_post
Restore a soft-deleted post. Posts can only be restored within 30 days of deletion.
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID to restore |
Scope: write:feedback + team role
restore_post({ postId: "post_01abc..." })
merge_post
Merge a duplicate post into a canonical post. Votes are combined. Reversible via unmerge_post.
| Parameter | Type | Description |
|---|---|---|
duplicatePostId | string | Post TypeID of the duplicate to merge away |
canonicalPostId | string | Post TypeID of the canonical post to merge into |
Scope: write:feedback + team role
Use search to find potential duplicates first, then merge them to consolidate votes.
merge_post({
duplicatePostId: "post_01abc...",
canonicalPostId: "post_01xyz..."
})
unmerge_post
Restore a merged post to independent state. Recalculates vote counts for both posts.
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID of the merged post to restore |
Scope: write:feedback + team role
unmerge_post({ postId: "post_01abc..." })
create_changelog
Create a changelog entry. Saves as a draft by default. Set publish: true to publish immediately.
| Parameter | Type | Default | Description |
|---|---|---|---|
title | string | - | Changelog entry title (max 200 characters) |
content | string | - | Changelog content, markdown supported (max 50,000 characters) |
publish | boolean | false | Set to true to publish immediately |
Scope: write:changelog + team role
create_changelog({ title: "v2.1 Release", content: "## New features\n- Dark mode..." })
create_changelog({ title: "v2.1 Release", content: "...", publish: true })
update_changelog
Update an existing changelog entry's title, content, publish state, or linked posts.
| Parameter | Type | Description |
|---|---|---|
changelogId | string | Changelog TypeID to update |
title | string | New title (max 200 characters) |
content | string | New content, markdown supported (max 50,000 characters) |
publish | boolean | true to publish, false to revert to draft |
linkedPostIds | string[] | Replace linked posts with these post TypeIDs |
Scope: write:changelog + team role
update_changelog({ changelogId: "changelog_01abc...", title: "v2.1.1 Patch" })
update_changelog({
changelogId: "changelog_01abc...",
publish: true,
linkedPostIds: ["post_01a...", "post_01b..."]
})
delete_changelog
Soft-delete a changelog entry.
| Parameter | Type | Description |
|---|---|---|
changelogId | string | Changelog TypeID to delete |
Scope: write:changelog + team role
delete_changelog({ changelogId: "changelog_01abc..." })
accept_suggestion
Accept a post-to-post merge suggestion, merging the source post into the target. Use swapDirection to reverse which post is kept.
| Parameter | Type | Description |
|---|---|---|
id | string | Merge suggestion TypeID (post_merge_sug_xxx) |
swapDirection | boolean | Optional. Reverse which post is kept |
Scope: write:feedback + team role
accept_suggestion({ id: "post_merge_sug_01abc..." })
accept_suggestion({ id: "post_merge_sug_01abc...", swapDirection: true })
dismiss_suggestion
Dismiss a merge suggestion. Restore it later with restore_suggestion.
| Parameter | Type | Description |
|---|---|---|
id | string | Merge suggestion TypeID (post_merge_sug_xxx) |
Scope: write:feedback + team role
dismiss_suggestion({ id: "post_merge_sug_01abc..." })
restore_suggestion
Restore a dismissed merge suggestion back to pending.
| Parameter | Type | Description |
|---|---|---|
id | string | Merge suggestion TypeID (post_merge_sug_xxx) |
Scope: write:feedback + team role
restore_suggestion({ id: "post_merge_sug_01abc..." })
get_post_activity
Get the activity log for a post. Shows status changes, merges, tag changes, owner assignments, proxy votes, comment pins, and other events in reverse chronological order (limited to the 200 most recent).
| Parameter | Type | Description |
|---|---|---|
postId | string | Post TypeID |
Scope: read:feedback + team role
Each activity entry includes the event type, actor name, metadata (e.g., old/new status), and timestamp.
get_post_activity({ postId: "post_01abc..." })
Help Center Tools
Help center tools are only available when the Help Center module is on in Admin → Settings → General. See Help Center for setup.
create_article
Create a new help center article as a draft.
| Parameter | Type | Description |
|---|---|---|
categoryId | string | Category TypeID to place the article in |
title | string | Article title (max 200 characters) |
slug | string | URL slug (lowercase, hyphens only) |
content | string | Article body in Markdown |
description | string | Short description for search results and SEO |
Scope: write:article + team role
Articles are created as drafts. Use update_article with publish: true to publish.
update_article
Update an existing help center article. Publish or unpublish by setting the publish parameter.
| Parameter | Type | Description |
|---|---|---|
articleId | string | Article TypeID to update |
title | string | New title |
content | string | New content in Markdown |
description | string | New description |
slug | string | New URL slug |
categoryId | string | Move to a different category |
publish | boolean | true to publish, false to unpublish |
Scope: write:article + team role
All fields except articleId are optional. Only provided fields are updated.
delete_article
Soft-delete a help center article.
| Parameter | Type | Description |
|---|---|---|
articleId | string | Article TypeID to delete |
Scope: write:article + team role
manage_category
Create, update, or delete a help center category.
| Parameter | Type | Description |
|---|---|---|
action | "create" | "update" | "delete" | Operation to perform |
categoryId | string | Category TypeID (required for update/delete) |
name | string | Category name (required for create) |
slug | string | URL slug (required for create) |
description | string | Category description |
icon | string | Icon identifier |
parentId | string | Parent category TypeID for nesting |
Scope: write:article + team role
Conversation Tools
Work the support inbox programmatically: list and read conversations, reply to visitors, and change conversation status. All conversation tools require a team role and the support inbox to be enabled.
list_conversations
List support-inbox conversations, newest activity first. Returns a compact, paginated list.
| Parameter | Type | Default | Description |
|---|---|---|---|
status | "open" | "snoozed" | "closed" | - | Filter by conversation status |
priority | "none" | "low" | "medium" | "high" | "urgent" | - | Filter by priority |
assignedAgentPrincipalId | string | - | Filter to a specific assigned agent (principal TypeID) |
limit | number (1-100) | 20 | Max results per page |
cursor | string | - | Pagination cursor from a previous response |
Scope: read:chat + team role
list_conversations({ status: "open" })
list_conversations({ assignedAgentPrincipalId: "principal_01xyz...", priority: "high" })
get_conversation
Get a conversation and its most recent messages.
| Parameter | Type | Default | Description |
|---|---|---|---|
conversationId | string | - | Conversation TypeID |
includeInternal | boolean | false | Include agent-only internal notes |
cursor | string | - | Cursor from a previous response to fetch older messages |
Scope: read:chat + team role
Internal notes are excluded unless includeInternal is true. Anonymous visitors' synthetic emails are never returned.
get_conversation({ conversationId: "conversation_01abc..." })
get_conversation({ conversationId: "conversation_01abc...", includeInternal: true })
reply_to_conversation
Send an agent reply in a conversation (visible to the visitor). Auto-assigns the conversation to the calling agent if it's unassigned.
| Parameter | Type | Description |
|---|---|---|
conversationId | string | Conversation TypeID |
content | string | Reply text sent to the visitor (max 4000 characters) |
fileIds | string[] | Optional. Up to 10 file IDs from upload_file to attach |
Scope: write:chat + team role
reply_to_conversation({ conversationId: "conversation_01abc...", content: "Thanks for reaching out, we're on it." })
set_conversation_status
Change a conversation's status. Snoozing defers it until the next visitor reply; closing stamps the resolution time; a later reply reopens it.
| Parameter | Type | Description |
|---|---|---|
conversationId | string | Conversation TypeID |
status | "open" | "snoozed" | "closed" | New status |
Scope: write:chat + team role
set_conversation_status({ conversationId: "conversation_01abc...", status: "closed" })
suggest_post
Suggest to the support team (not the visitor) that a resolved conversation be tracked as a feedback post. Appears as an internal note; a teammate confirms with one click.
| Parameter | Type | Description |
|---|---|---|
conversationId | string | Conversation TypeID (must be resolved) |
boardId | string | Suggested board TypeID |
title | string | Suggested post title |
content | string | Suggested post body |
Scope: write:chat + team role
suggest_post({ conversationId: "conversation_01abc...", boardId: "board_01abc...", title: "Add dark mode", content: "Customer asked for a night theme." })
share_post
Embed an existing feedback post as a card in the conversation so the visitor can view and upvote it.
| Parameter | Type | Description |
|---|---|---|
conversationId | string | Conversation TypeID |
postId | string | Post TypeID |
Scope: write:chat + team role
share_post({ conversationId: "conversation_01abc...", postId: "post_01xyz..." })
Ticket Tools
Tickets share the conversation scopes: reads need read:chat, writes need write:chat. All ticket tools require a team role.
list_tickets
List support tickets. A service key sees every ticket; a human caller sees the tickets their role can view.
| Parameter | Type | Description |
|---|---|---|
type | "customer" | "back_office" | "tracker" | Filter by ticket type |
ticketTypeId | string | Filter by registry ticket-type TypeID |
statusCategory | "open" | "pending" | "closed" | Filter by internal status category |
stage | "received" | "in_progress" | "awaiting_requester" | "resolved" | Filter by customer-facing stage |
requesterPrincipalId | string | Filter to a requester |
companyId | string | Filter to a company |
sort | "recent" | "oldest" | "created" | "priority" | Sort order (default recent) |
limit | number (1-100) | Max results (default 20) |
Scope: read:chat + team role
list_tickets({ type: "customer", statusCategory: "open" })
get_ticket
Get one ticket and its thread.
Scope: read:chat + team role
get_ticket({ ticketId: "ticket_01abc..." })
create_ticket
Open a support ticket. type is customer (a requester's request), back_office (an internal task), or tracker (an umbrella others link to).
| Parameter | Type | Description |
|---|---|---|
type | "customer" | "back_office" | "tracker" | Ticket object type (default customer) |
ticketTypeId | string | Registry ticket-type TypeID |
title | string | Short summary |
description | string | Opening message body (Markdown) |
priority | "none" | "low" | "medium" | "high" | "urgent" | Triage priority |
requesterPrincipalId | string | Requester principal TypeID |
companyId | string | Associated company TypeID |
Scope: write:chat + team role
create_ticket({ type: "customer", title: "Refund not received", description: "Customer reports a missing refund." })
reply_to_ticket
Post a reply on a ticket thread (visible to the requester). Pass up to 10 fileIds from upload_file to attach files.
Scope: write:chat + team role
reply_to_ticket({ ticketId: "ticket_01abc...", content: "We've issued your refund." })
add_ticket_note
Add an internal note. Never visible to the requester. Pass up to 10 fileIds from upload_file to attach files.
Scope: write:chat + team role
add_ticket_note({ ticketId: "ticket_01abc...", content: "Confirmed with billing." })
link_ticket
Link a customer ticket to a tracker so the tracker's stage changes cascade onto it.
Scope: write:chat + team role
link_ticket({ trackerTicketId: "ticket_01tracker...", ticketId: "ticket_01customer..." })
unlink_ticket
Remove a customer ticket from a tracker.
Scope: write:chat + team role
unlink_ticket({ trackerTicketId: "ticket_01tracker...", ticketId: "ticket_01customer..." })
File and Widget Tools
upload_file
Upload a file so you can attach it to a conversation reply, ticket reply, or ticket note. Returns a fileId to pass in fileIds on reply_to_conversation, reply_to_ticket, or add_ticket_note.
| Parameter | Type | Description |
|---|---|---|
name | string | File name, including its extension |
contentBase64 | string | File bytes, base64-encoded (max 5 MB decoded) |
Scope: write:chat + team role
upload_file({ name: "invoice.pdf", contentBase64: "JVBERi0xLjQK..." })
widget_install_status
Check whether the widget is connected on your site. Returns whether the snippet has been seen, whether the widget is enabled, the last detected time, the origin host, and the SDK version. Never returns the signing secret.
Scope: read:feedback + team role
widget_install_status()
Resources
Resources provide lookup data for boards, statuses, tags, roadmaps, team members, and help-center categories. Use them to find IDs before calling tools like triage_post or create_post.
Feedback resources require read:feedback. The help-center category list requires read:article and a team role.
| URI | Scope | Returns | Fields |
|---|---|---|---|
quackback://boards | read:feedback | All boards | id, name, slug |
quackback://statuses | read:feedback | All statuses | id, name, slug, color |
quackback://tags | read:feedback | All tags | id, name, color |
quackback://roadmaps | read:feedback | All roadmaps | id, name, slug |
quackback://members | read:feedback | Team members | id, name, role |
quackback://help-center/categories | read:article + team | Categories with article counts | id, slug, name, description, icon, parentId, isPublic, position, articleCount |
A typical workflow: read quackback://statuses to find the "planned" status ID, then call triage_post with that status ID.