Skip to Content
MCP ServerMCP Server

MCP Server

The SupDesk MCP (Model Context Protocol) server gives AI assistants and other LLM-powered tools direct access to a project’s help center, feedback board, changelog, message threads, beta programs, and waitlist.

Endpoint

https://mcp.supdesk.app/mcp

Authentication

Pass an API key as a Bearer token:

Authorization: Bearer sd_live_...

Generate keys in the SupDesk console under Workspace Settings → API Keys.

The key determines the project, so no tool takes a project_id argument — there’s nothing for a model to guess or get wrong.

What you get

Which tools appear depends on the project. A feature switched off in Project Settings has its tools omitted from tools/list entirely, exactly as the REST API 404s those routes.

GroupToolsRequires
Help center10Help center enabled
Feedback board5Feedback board enabled
Changelog5Changelog enabled
Messages5Private messages enabled
Beta testing8Beta enabled
Waitlist4Waitlist enabled
Overview1Always available

Reads work on every plan. Writes require a paid plan — a create, update, or delete on a read-only key returns forbidden, while reads on the same key work fine.

Help center

  • list_articles — articles, newest edit first. Includes drafts and archived articles; filter with status for what customers can see.
  • get_article — one article by id, including its Markdown body.
  • search_articles — full-text search across published articles, ranked, with a snippet.
  • create_article — creates a draft. Publishing is a separate call, so nothing reaches customers by accident.
  • update_article — patch any field; set status to published or archived.
  • delete_article — permanent; archive instead to retire an article you want to keep.
  • list_article_categories, create_article_category, update_article_category, delete_article_category — deleting a category leaves its articles in place, uncategorised.

Article bodies are Markdown, not HTML. The portal renders them through a viewer that never evaluates HTML, so HTML arrives at readers as literal text.

create_article fails with limit_reached once the plan’s article allowance is spent — Free 5, Pro 50, Team unlimited.

Feedback board

  • list_feedback — posts, newest first. Covers all three types (bug, feature, feedback) unless you narrow it with type.
  • get_feedback, create_feedback, update_feedbackupdate_feedback is how you move a post along open → planned → in_progress → done.
  • delete_feedback — removes the post, its votes, and its comments.

Changelog, messages, beta, and waitlist

Full parity with the REST API, over the same queries:

  • Changeloglist_changelog, get_changelog_entry, create_changelog_entry, update_changelog_entry, delete_changelog_entry. Entries default to draft, because publishing is what emails your subscribers.
  • Messageslist_threads, get_thread (with the full message history), create_thread, reply_to_thread, update_thread.
  • Beta testinglist_beta_programs, get_beta_program, create_beta_program, update_beta_program, delete_beta_program, list_beta_testers, add_beta_tester, remove_beta_tester. add_beta_tester is idempotent on email and returns the accept token; this server sends no email, so delivering the invite is yours to do.
  • Waitlistlist_waitlist, add_waitlist_signup (idempotent on email), update_waitlist_signup, remove_waitlist_signup.

Overview

project_stats returns counts across the project — feedback posts by status, open threads, published and draft articles, and the helpful / not-helpful votes those articles have collected — so an agent can answer “how much is outstanding?” without walking four paginated lists.

Structured output

Every tool declares an outputSchema and returns both structuredContent (typed, validated) and a content text block holding the same data as JSON. Parse whichever your client supports.

const result = await mcpClient.callTool("get_article", { id: "8f7a..." }); result.structuredContent.article.title; // typed JSON.parse(result.content[0].text); // same payload, fallback

Annotations

Every tool carries annotations so a client knows what it’s invoking before it invokes it:

AnnotationSet on
readOnlyHint: truelist_*, get_*, search_*, project_stats
idempotentHint: truereads and update_*
destructiveHint: truedelete_*, remove_*

Pagination

List tools take an opaque cursor and return a next_cursor — pass it back for the next page, and stop when it’s null.

let cursor; do { const page = await mcpClient.callTool("list_articles", { status: "published", cursor }); handle(page.structuredContent.data); cursor = page.structuredContent.next_cursor; } while (cursor);

A cursor is a token, not arithmetic — treat it as opaque. A malformed one returns invalid_request rather than silently restarting from page one, which would make a paginated walk loop forever.

Note that this is a convention in each tool’s own argument schema. MCP’s protocol-level cursor applies to listings of tools and resources, not to what a tool returns; the resource listing below uses the real thing.

Resources

For attaching project content as context, rather than calling a tool and pasting the result:

URIContents
supdesk://articles/{slug}A published help article, as Markdown.
supdesk://boardThe project’s open feedback posts, as JSON.

The article listing is genuinely protocol-paginated, and covers published articles only — a resource is content a client may surface verbatim, and a draft is unpublished for a reason.

Prompts

Two starting points, wired to the project’s real data:

  • draft_article_from_thread — turns a resolved support conversation into a help-center article, so the next customer with that question finds the answer instead of opening a thread. Needs the help center and private messages enabled.
  • triage_feedback — assesses one post and recommends a status, with the reasoning. It checks search_articles first, so an existing answer is surfaced instead of new work being proposed.

Both use MCP’s completion capability for their arguments: as you type a category, the server answers with the project’s actual category slugs rather than making you guess.

Logging

Multi-step writes send progress notifications over MCP logging — create_article checks the plan allowance, probes for a free slug, then inserts, and says so at each step. A client without logging support is unaffected; the write proceeds either way.

Examples

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{ "mcpServers": { "sup-desk": { "command": "npx", "args": [ "-y", "mcp-remote", "https://mcp.supdesk.app/mcp", "--header", "Authorization: Bearer sd_live_..." ] } } }

Answer a question, or write the answer

// Check whether the help center already covers it. const hits = await mcpClient.callTool("search_articles", { query: "reset password", limit: 5 }); if (hits.structuredContent.data.length === 0) { // Nothing yet — draft one. It's created as a draft. const created = await mcpClient.callTool("create_article", { title: "How do I reset my password?", body: "Open **Settings**, then choose *Reset password*.", category_id: "3c2b..." }); // Publishing is deliberate and separate. await mcpClient.callTool("update_article", { id: created.structuredContent.article.id, status: "published" }); }

Triage the board

const open = await mcpClient.callTool("list_feedback", { status: "open", limit: 10 }); await mcpClient.callTool("update_feedback", { id: open.structuredContent.data[0].id, status: "planned" });

Everything outstanding, in one call

const stats = await mcpClient.callTool("project_stats", {}); // { posts_by_status: { open: 12, planned: 3, ... }, open_threads: 4, // articles_published: 18, articles_draft: 2, // article_helpful: 240, article_not_helpful: 11 }

Rate limits

The MCP server shares the REST API’s limits: 120 requests per 60 seconds per project. See Rate Limits & Usage.

Errors

A failing tool call returns isError: true with the same error vocabulary the REST API uses:

{ "error": { "code": "limit_reached", "message": "Your plan allows 5 help-center articles." } }
CodeMeaning
unauthorizedMissing, malformed, revoked, or unknown API key.
forbiddenValid key, but the plan has no API write access.
invalid_requestBad arguments — including a malformed cursor.
not_foundNo such id in this project.
limit_reachedA plan allowance is spent (for example the article cap).
rate_limitedToo many requests in the current window.
internal_errorSomething failed on our side. Safe to retry.

The full list is documented under Errors.

Last updated on