MCP Server

The Helmdesk MCP server lets AI assistants (Claude Desktop, Cursor, Windsurf, or any MCP-compatible client) interact with your helpdesk using natural language. Your agent can triage tickets, draft replies, search articles, and send emails — no code required.

Setup

1. Create a scoped API key

Go to your project's API Keys page in the dashboard and create a key with the MCP / AI assistant preset. This grants tickets, articles, feedback, customers, email sending and template management, AI, and log read/write scopes — everything the assistant tools use except webhooks:manage, which you can add explicitly if you want your agent to manage webhook endpoints. See the full scope table below.

2. Add to your MCP client

Claude Desktop

Add this to your claude_desktop_config.json:

{
  "mcpServers": {
    "helmdesk": {
      "command": "npx",
      "args": ["-y", "@helmdesk/mcp"],
      "env": {
        "HELMDESK_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}

Cursor / VS Code

Add to your .cursor/mcp.json or VS Code MCP settings:

{
  "mcpServers": {
    "helmdesk": {
      "command": "npx",
      "args": ["-y", "@helmdesk/mcp"],
      "env": {
        "HELMDESK_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}

Claude Code (CLI)

claude mcp add helmdesk -- npx -y @helmdesk/mcp
# Then set the env var:
export HELMDESK_API_KEY="sk_live_your_key_here"

Codex CLI

Add the server to your ~/.codex/config.toml (note: Codex uses TOML, and the key is mcp_servers with an underscore):

[mcp_servers.helmdesk]
command = "npx"
args = ["-y", "@helmdesk/mcp"]
env = { HELMDESK_API_KEY = "sk_live_your_key_here" }

Or add it from the terminal:

codex mcp add helmdesk --env HELMDESK_API_KEY=sk_live_your_key_here -- npx -y @helmdesk/mcp

3. Start asking

Once connected, just ask your AI assistant to work with your helpdesk. It will call the right tools automatically.

Example prompts

These are real things you can say. The MCP server translates each into the right API calls.

Triage open tickets

"Show me all open tickets sorted by oldest first. For any waiting more than 24 hours, draft a reply and set them to pending."

list_ticketsget_ticketreply_to_ticketupdate_ticket

Answer with knowledge base

"A customer is asking about file upload limits. Find the relevant KB article and reply with a link."

get_ticketsearch_articlesreply_to_ticket

Classify and prioritize

"Go through all new tickets. Anything mentioning 'down', 'broken', or 'outage' should be urgent. Everything else medium."

list_ticketsupdate_ticket

Send an email

"Send a welcome email to jane@acme.com using the welcome template with her name."

get_template_schemasend_email

Daily summary

"Summarize my support queue: how many open tickets, what's the oldest, any urgent ones?"

list_tickets

Who is this person?

"jane@acme.com just emailed asking for a call. Look her up — what has she asked us before, how did she rate us, and are there any internal notes?"

list_customersget_customer

Bulk resolution

"Find all tickets tagged 'known-issue' that are still open, reply with the fix from our KB, and resolve them."

list_ticketsget_ticketsearch_articlesreply_to_ticketupdate_ticket

Tool reference

The MCP server exposes these tools. Your AI assistant discovers them automatically — you don't need to call them directly.

list_tickets

List support tickets with optional filters.

statusstring?new, open, pending, resolved, closed
prioritystring?low, medium, high, urgent
searchstring?Search by subject or email
numbernumber?Exact ticket number ("#42") lookup
pagenumber?Page number (default: 1)
limitnumber?Results per page (max: 100)
get_ticket

Get a ticket with its full message thread, tags, and customer info.

ticketIdstringThe ticket UUID
create_ticket

Create a new ticket on behalf of a customer.

subjectstringTicket subject
bodystringInitial message body
customerEmailstringCustomer email
customerNamestring?Customer name
prioritystring?low, medium, high, urgent
categorystring?bug, feature_request, billing, howto, other
update_ticket

Update a ticket's status, priority, category, or assignee.

ticketIdstringThe ticket UUID
statusstring?New status
prioritystring?New priority
categorystring?New category
assignedTostring?Member UUID (see list_team_members), or "none" to unassign
reply_to_ticket

Send a staff reply to a ticket. The reply is emailed to the customer and a new ticket transitions to open.

ticketIdstringThe ticket UUID
bodystringReply message
add_ticket_tag

Add a tag (label) to a ticket for categorization or triage. Returns the full tag list.

ticketIdstringThe ticket UUID
tagstringTag to add (1–50 characters)
remove_ticket_tag

Remove a tag from a ticket. Returns the remaining tags.

ticketIdstringThe ticket UUID
tagstringTag to remove
list_team_members

List the active members of your account — the valid assignedTo targets for update_ticket.

list_email_templates

List the project's email templates and the account's shared templates. Use this first to discover valid templateKey values.

import_email_template

Create or update an email template from Handlebars source. Same key = new version; new key = new template. To brand a template, import the layout first and pass its key as layout — then check layoutLinked in the result.

keystringTemplate key, e.g. "welcome"
bodystringHandlebars source
subjectstring?Subject line (may contain variables)
namestring?Display name
typestring?template, layout, or partial
layoutstring?Key of an existing layout to wrap the template in. Omit to keep the current one; "" removes it.
send_email

Send a transactional email using a template.

templateKeystringTemplate key (e.g. "welcome")
toobject{ email, name? }
variablesobject?Template variables
send_email_batch

Send up to 100 transactional emails in one request. Items are delivered independently — a failed item is reported in its result slot without failing the rest. Requires the Pro plan or higher.

emailsarrayItems: { templateKey, to_email, to_name?, variables?, idempotencyKey? }
preview_email

Preview a rendered email without sending it.

templateKeystringTemplate key
variablesobject?Template variables
get_template_schema

Get the variable schema for a template, so you know what variables to pass.

templateKeystringTemplate key
search_articles

Search the knowledge base for articles matching a query.

querystring?Search query
categorystring?Filter by category
create_article

Create a knowledge-base article (defaults to draft; pass status 'published' to make it live).

titlestringArticle title
bodystring?Markdown body
slugstring?URL slug (auto-generated when omitted)
excerptstring?Short summary
categoryIdstring?Category UUID (see list_article_categories)
statusstring?draft or published
get_article

Get a single article by ID, including its full markdown body.

articleIdstringThe article UUID
update_article

Update an article's content, category, or status — including publishing a draft.

articleIdstringThe article UUID
titlestring?New title
bodystring?New markdown body
slugstring?New URL slug
excerptstring?New summary
categoryIdstring?Category UUID, or "none" to clear
statusstring?draft or published
list_article_categories

List the project's article categories — the valid categoryId values.

list_webhooks

List the project's webhook endpoints (secrets are never included).

create_webhook

Create a webhook endpoint. The signing secret is returned only once, in this response.

urlstringHTTPS endpoint to deliver events to
eventsarrayticket.created, ticket.resolved, message.received, feedback.submitted, email.bounced, customer.created, customer.updated
slackFormatboolean?Format payloads for Slack
activeboolean?Start active (default: true)
delete_webhook

Delete a webhook endpoint. Deliveries stop immediately.

webhookIdstringThe webhook UUID
write_log

Ship log events to the project (they group into log issues). With a sandbox key, events are quota-exempt.

eventsarrayItems: { level, message, scope?, metadata? }
list_log_issues

List grouped error issues from your application logs — the fastest way for an assistant to answer “did anything break?”

statusstring?open, acknowledged, resolved (comma-separated)
levelstring?Filter by level, e.g. "error,fatal"
searchstring?Match sample message or scope
query_logs

Query raw log events, newest first. Pass a fingerprint from list_log_issues to see every occurrence of one error group.

levelstring?Filter by level, e.g. "error,fatal"
searchstring?Match message or scope
fingerprintstring?Error-group fingerprint
dateFromstring?ISO date lower bound
dateTostring?ISO date upper bound
list_feedback_conversations

List feedback conversations (customer feedback threads) with optional filters, newest first. Each has a title, status, optional 1–5 rating, source, and customer info.

statusstring?open or closed
ratingnumber?Exact customer rating (1–5)
sourcestring?request, api, widget, dashboard
customerEmailstring?Filter by customer email
searchstring?Match title or customer email/name
dateFromstring?ISO date lower bound
dateTostring?ISO date upper bound
pagenumber?Page number (default: 1)
limitnumber?Results per page (max: 100)
get_feedback_conversation

Get a feedback conversation's full detail: the complete message thread (with attachments), rating, and status. Read the whole exchange before replying.

conversationIdstringThe feedback conversation UUID
reply_to_feedback

Post a staff reply to a feedback conversation. The reply is recorded on the thread and emailed to the customer with a link back to the conversation.

conversationIdstringThe feedback conversation UUID
bodystringReply message (max 10000 characters)
close_feedback_conversation

Close a feedback conversation once it has been handled, or pass status 'open' to reopen it. Closing does not notify the customer.

conversationIdstringThe feedback conversation UUID
statusstring?Target status (default: "closed"; pass "open" to reopen)
list_feedback_requests

List outgoing feedback requests with their funnel status, newest first: sent (emailed) → opened (respond page visited) → responded (conversation created).

statusstring?sent, opened, responded
customerEmailstring?Filter by customer email
searchstring?Match title or customer email/name
dateFromstring?ISO date lower bound
dateTostring?ISO date upper bound
pagenumber?Page number (default: 1)
limitnumber?Results per page (max: 100)
request_feedback

Ask a customer for feedback. Creates a feedback request and immediately emails the customer a branded message with a respond link — when they answer, a feedback conversation is created. With a sandbox key the email is captured, not delivered.

titlestringShort internal title, e.g. "Post-onboarding check-in"
messagestringThe message/question the customer receives
customerEmailstringCustomer email to send the request to
customerNamestring?Customer name (used in the email greeting)
list_customers

List/search the project's customer directory, most recently seen first. The directory builds itself from tickets and feedback — this is everyone who has interacted with the project.

searchstring?Substring match on email, name, or company
externalIdstring?Exact match on YOUR app's user id for the customer
pagenumber?Page number (default: 1)
limitnumber?Results per page (max: 100)
get_customer

Get a customer with their full interaction history — tickets and feedback conversations, recent-first. The best way for an assistant to answer "who is this person and what have they asked us before?" in one call.

customerIdstringThe customer UUID (find it via list_customers)
create_customer

Create a customer explicitly (they're also created automatically from tickets and feedback). Fails with a 409 if the email already exists in this environment.

emailstringCustomer email — unique, immutable after creation
namestring?Display name
companystring?Company name
externalIdstring?YOUR app's user id for the customer
internalNotestring?Staff-only note, never shown to the customer
metadataobject?Arbitrary JSON (max 8 KB serialized)
update_customer

Update a customer's profile. Email is immutable; pass null to clear a field. internalNote is a staff-only, CRM-style note — use it to record context like 'churn risk, complained about pricing in March'.

customerIdstringThe customer UUID
namestring?Display name (null clears)
companystring?Company name (null clears)
externalIdstring?YOUR app's user id (null clears)
internalNotestring?Staff-only CRM-style note (null clears)
metadataobject?Arbitrary JSON (null clears)
delete_customer

Remove a customer from the directory. Their tickets and feedback are untouched (linkage is by email), and a new interaction re-creates the customer.

customerIdstringThe customer UUID

Resources

MCP resources provide read-only data snapshots that AI assistants can access directly without calling a tool.

helmdesk://tickets/open

All open tickets — gives the AI instant context about your queue.

helmdesk://tickets/pending

Tickets waiting on a customer reply — useful for follow-up workflows.

helmdesk://articles

Published knowledge base articles — the AI uses these to find answers before drafting replies.

API key scopes

For MCP use, we recommend creating a scoped API key that only grants the permissions your AI assistant needs. The MCP / AI assistant preset in the dashboard is a good starting point.

ScopeGrantsMCP preset
tickets:readList and view tickets, list team membersIncluded
tickets:writeCreate, update, and reply to ticketsIncluded
articles:readSearch/read knowledge base + categoriesIncluded
articles:writeCreate and edit articlesIncluded
emails:sendSend emails, list templates, previewIncluded
emails:manageImport/update email templatesIncluded
webhooks:manageList/create/delete webhook endpoints
logs:readQuery log events and grouped log issuesIncluded
logs:writeIngest application log eventsIncluded
feedback:readList/view feedback conversations and requestsIncluded
feedback:writeReply, close/reopen, send feedback requestsIncluded
customers:readList/view customers and their interaction historyIncluded
customers:writeCreate, update, and delete customersIncluded

You can also restrict keys by IP address. See the IP Allowlist section when creating a key.

Troubleshooting

"API key lacks the required scope"

The key you're using doesn't have permission for the operation your AI tried. Go to the API Keys page in your dashboard, click the settings icon on the key, and add the missing scope.

"IP address is not allowed"

The request came from an IP not in the key's allowlist. Either add the IP to the key, or remove the allowlist to allow all IPs.

MCP server not connecting

Make sure you have Node.js 18+ installed and npx available in your PATH. You can test the server manually:

HELMDESK_API_KEY=sk_live_... npx @helmdesk/mcp

If the server starts, the issue is in your MCP client config. Check that the JSON is valid and the env var is set correctly.