How to add customer support to an MCP server
To add customer support to an MCP server, mount three tools inside the server: support_ask answers from your own rules in code, support_open_ticket sends a ticket to a help desk with a context block your server attaches, and support_ticket_status reads the team's reply back. The customer stays in Claude, ChatGPT or Cursor, and nobody describes the problem twice.
The three tools
support_ask: answers from your own rules, in code. Lists the account's recent errors. Unknown questions point at the ticket.support_open_ticket: opens a ticket with the customer's name and email, a subject, a category and the context block.support_ticket_status: reads the ticket and the team's public replies back, after checking the ticket belongs to the caller.
Step by step
- 1. Record each tool call
In your tool wrapper, keep the last 12 calls per account: tool name, time, duration, ok or not, and the error text. Keep it in process; it is only used to build the context block.
- 2. Mount support_ask
Match the question against rules you already know (credits, connection, permissions, rate limits) and answer with the tool the agent should call next. Make no model call. If no rule matches, say so and point at support_open_ticket. Never invent an answer.
- 3. Mount support_open_ticket
Take a subject, a message, a priority and a category. Add the context block under the customer's words and POST the ticket to your canhelpto workspace with your key and X-Service-Mode: true.
- 4. Mount support_ticket_status
GET the ticket without service mode, so internal notes stay out. Show it only if metadata.mcp.account.userId or the customer email matches the caller.
- 5. Tell the agent when to call them
Add one sentence to your server's instructions, and a support hint to every error body: if this keeps failing, call support_ask; support_open_ticket puts it to the team with this error attached.
The context block
Your server knows what the customer's agent just tried. The customer does not. Attach it, and the person answering sees the failed call next to the question. This is an example block:
- Connector context -
Account: [email protected] · user 7f3e…
Client: claude-user/1.0 · catalogue 1.8.0
Last calls:
- 16:05:00 approve_drafts · 120 ms · FORBIDDEN: managed accountOpen the ticket
// support_open_ticket, the part that matters (TypeScript)
const ctx = { account: user, client: { userAgent }, recentCalls: recent.get(user.id) ?? [] };
const res = await fetch("https://canhelpto.com/api/v1/tickets", {
method: "POST",
headers: { "X-API-Key": process.env.CANHELPTO_API_KEY!, "X-Service-Mode": "true", "Content-Type": "application/json" },
body: JSON.stringify({
summary: input.subject,
message: input.message + "\n\n" + renderContext(ctx), // words first, context under them
customerName: user.name,
customerEmail: user.email,
priority: input.priority,
category: input.category,
tags: ["mcp", input.category],
page: "mcp://your-server",
metadata: { mcp: ctx }, // comes back on read
}),
});Questions
- Does support_ask need a model?
- No. It answers from rules in your code. That keeps it fast, free per call and predictable. A question with no matching rule points at the ticket.
- What is in the context block?
- The account, the client user-agent, your catalogue version and the last tool calls with their errors. It sits under the customer's words in the ticket and also in metadata.mcp.
- How does the customer get the answer?
- Your team replies in the canhelpto inbox. The reply goes out by email and the customer's agent can read it with support_ticket_status.
- Is it free to start?
- Yes. The free plan covers one workspace and unlimited tickets. See the pricing page for what Pro adds.
Full contract, REST calls and the TypeScript wrapper: docs for agents. Plans: pricing.