@markusvankempen/zendesk-mcp-server
v1.0.2
Published
Zendesk MCP server — 14 support ticket tools for AI agents. Cursor, VS Code, Claude Desktop, watsonx Orchestrate, IBM Code Engine SSE.
Maintainers
Keywords
Readme
@markusvankempen/zendesk-mcp-server
TypeScript MCP server for Zendesk support operations — 14 tools for AI agents to create, search, assign, comment, and close tickets with correct customer requester attribution.
mcp · zendesk · cursor · claude · github-copilot · ai-agents · customer-support · typescript
Author: Markus van Kempen
Email: [email protected] · [email protected]
Website: markusvankempen.github.io
No bug too small, no syntax too weird.
Table of contents
- What it does
- Install
- Environment variables
- Quick start — Cursor
- Quick start — Claude Desktop
- Quick start — VS Code
- Tools available
- SSE mode — IBM Code Engine / remote
- Build from source
- Project structure
- How the server works
- Requester attribution
- Error handling
- Verify tool registration
- Troubleshooting
- Documentation
What it does
This server exposes 14 Zendesk API operations as MCP tools that any MCP-compatible AI agent can call. The most important feature is correct ticket requester attribution: when create_ticket is called, the server looks up or creates the actual customer user in Zendesk and sets them as the ticket requester — not the API service account. This ensures Zendesk reply emails route to the customer inbox.
The server supports two transports:
- stdio (default) — spawn as child process by IDE or WxO
- SSE — run as HTTP server for remote clients (IBM Code Engine)
Install
# Via npx (no install needed — recommended for mcp.json)
npx -y @markusvankempen/zendesk-mcp-server
# Global install
npm install -g @markusvankempen/zendesk-mcp-server
zendesk-mcp-server
# As project dependency
npm install @markusvankempen/zendesk-mcp-serverRequires Node.js 18 or later.
Environment variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| ZENDESK_SUBDOMAIN | Yes | — | Your Zendesk tenant prefix. mycompany → mycompany.zendesk.com |
| ZENDESK_EMAIL | Yes | — | Service account email used for API authentication |
| ZENDESK_API_TOKEN | Yes | — | Zendesk API token (Settings → Channels → API) |
| MCP_TRANSPORT | No | stdio | Set to sse to start an HTTP server instead of stdio |
| PORT | No | 8080 | HTTP port for SSE mode |
| HOST | No | 0.0.0.0 | HTTP bind address for SSE mode |
Getting a Zendesk API token:
- Log in to your Zendesk Admin UI
- Go to Admin → Apps and Integrations → APIs → Zendesk API
- Enable Token Access
- Click "Add API token"
- Copy the token (shown only once)
Quick start — Cursor
Add to ~/.cursor/mcp.json (global) or <project>/.cursor/mcp.json (project-level):
{
"mcpServers": {
"zendesk-mcp": {
"command": "npx",
"args": ["-y", "@markusvankempen/zendesk-mcp-server"],
"env": {
"ZENDESK_SUBDOMAIN": "mycompany",
"ZENDESK_EMAIL": "[email protected]",
"ZENDESK_API_TOKEN": "xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Restart Cursor after saving. The server will appear in the MCP tools panel. You can now ask Cursor:
- "Create a support ticket for [email protected] about a login issue"
- "Show me all open high-priority tickets"
- "What are the SLA metrics for ticket 42?"
Tip: Install the VS Code extension to manage this config file automatically with secure credential storage.
Quick start — Claude Desktop
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"zendesk-mcp": {
"command": "npx",
"args": ["-y", "@markusvankempen/zendesk-mcp-server"],
"env": {
"ZENDESK_SUBDOMAIN": "mycompany",
"ZENDESK_EMAIL": "[email protected]",
"ZENDESK_API_TOKEN": "xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Restart Claude Desktop after saving.
Quick start — VS Code
Using the VS Code extension (recommended):
- Install
zendesk-mcp-vscodefrom VS Marketplace or Open VSX - Run command
Zendesk MCP: Configure Credentials - Enter subdomain, email, and API token
- The extension writes
mcp.jsonautomatically
Manual setup — add to VS Code settings.json:
{
"mcp.servers": {
"zendesk-mcp": {
"command": "npx",
"args": ["-y", "@markusvankempen/zendesk-mcp-server"],
"env": {
"ZENDESK_SUBDOMAIN": "mycompany",
"ZENDESK_EMAIL": "[email protected]",
"ZENDESK_API_TOKEN": "xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Tools available
All 14 tools are registered automatically on startup. See docs/reference/TOOLS.md for full documentation of each tool including parameters, return JSON, error cases, and usage examples.
| Tool | What it does |
|------|-------------|
| create_ticket | Create ticket with correct customer requester attribution |
| get_ticket | Get full ticket details by ID |
| update_ticket | Change status, priority, tags, or add a comment |
| search_tickets | Search with Zendesk query syntax |
| list_recent_tickets | Most recently updated tickets |
| list_all_tickets | Admin-wide listing with optional status filter |
| list_tickets_for_requester | All tickets owned by a specific customer email |
| get_ticket_comments | Full conversation history (public + internal) |
| add_ticket_comment | Post a reply or internal note |
| get_user_by_email | Look up a Zendesk user record |
| close_ticket | Mark a ticket as solved |
| reopen_ticket | Reopen a solved/closed ticket |
| get_ticket_metrics | SLA and timing metrics |
| assign_ticket | Assign to a group and/or specific agent |
SSE mode — IBM Code Engine / remote
Run as an HTTP server for remote MCP clients:
ZENDESK_SUBDOMAIN=mycompany \
[email protected] \
ZENDESK_API_TOKEN=xxxx \
MCP_TRANSPORT=sse \
PORT=8080 \
zendesk-mcp-serverSSE endpoints
| Path | Method | Description |
|------|--------|-------------|
| GET /sse | GET | Opens a persistent SSE stream. Response stays open. Server sends event: endpoint with data: /message?sessionId=<uuid>. |
| POST /message?sessionId=<id> | POST | Send a JSON-RPC request. Response arrives via the SSE stream. |
| GET /health | GET | Returns 200 ok. Used by load balancers and Code Engine health probes. |
Remote MCP client configuration
{
"mcpServers": {
"zendesk-mcp-remote": {
"url": "https://your-app.us-south.codeengine.appdomain.cloud/sse"
}
}
}Note: URL-based MCP server config requires an MCP client that supports SSE transport. Cursor supports this in recent versions.
Test SSE locally
# Terminal 1 — start server
MCP_TRANSPORT=sse PORT=8080 ZENDESK_SUBDOMAIN=x [email protected] ZENDESK_API_TOKEN=x \
zendesk-mcp-server
# Terminal 2 — test health
curl http://localhost:8080/health
# → ok
# Terminal 3 — open SSE stream (stays open)
curl -N -H "Accept: text/event-stream" http://localhost:8080/sse
# → event: endpoint
# → data: /message?sessionId=abc123See deployments/code-engine/README.md for full IBM Code Engine deployment.
Distribution
This package is published to npm. Do not clone GitHub to run the server — use npx:
npx -y @markusvankempen/zendesk-mcp-serverServer source is maintained in a private development repository. See docs/PUBLIC_REPO.md.
Package layout (npm tarball)
@markusvankempen/zendesk-mcp-server/
├── bin/
│ └── zendesk-mcp-server.js # CLI shim — #!/usr/bin/env node
├── dist/ # Compiled JavaScript
├── package.json # npm package manifest with bin entry
└── tsconfig.json # TypeScript compiler configHow the server works
Startup sequence
1. Load ZendeskConfig (ZENDESK_SUBDOMAIN / EMAIL / API_TOKEN)
→ Throws immediately if any are missing
2. Create McpServer({ name: "zendesk-mcp", version: "1.0.0" })
3. Register all 14 tools via registerTicketTools(server)
4. Read MCP_TRANSPORT from environment
5a. stdio: create StdioServerTransport → server.connect(transport)
5b. sse: create http.createServer with /health, /sse, /message routes
→ server.listen(PORT, HOST)Credential handling
ZENDESK_EMAIL + ZENDESK_API_TOKEN
→ `${email}/token:${token}`
→ base64 encode
→ Authorization: Basic <encoded>This header is attached to every outgoing REST call inside authHeaders() in zendesk-client.ts.
Per-request call graph for create_ticket
LLM → MCP client → JSON-RPC tools/call create_ticket
→ TypeScript handler:
1. validateEmail(requester_email) — if false: return jsonError
2. zendeskFetch GET /users/search.json?query={email}
→ if found: extract users[0].id
→ if empty: zendeskFetch POST /users.json → get new id
3. zendeskFetch POST /tickets.json { requester_id: id, ... }
4. return jsonResult({ ticket_id, ticket_url, requester_email, subject, status })Requester attribution
This is the most important design decision. When an AI agent creates a Zendesk ticket using a service account, Zendesk defaults the requester to the service account — meaning all reply emails go to the bot inbox instead of the customer.
This server solves it by:
- Accepting
requester_emailas a required parameter increate_ticket - Looking up the customer in Zendesk by email
- Creating the customer as a Zendesk user if they don't exist (with
verified: true) - Setting
requester_idon the ticket to the customer's Zendesk user ID
The service account credentials appear only in the Authorization header. The customer's identity is explicit in the ticket data.
Error handling
All tools catch exceptions and return JSON error objects — no tool ever crashes the MCP server process or throws an unhandled exception at the protocol level:
{ "error": "404 Not Found — Couldn't find Ticket with 'id'=99999" }Error sources:
- Missing required parameters → validated before any API call
- Invalid email format → validated with regex before any API call
- Zendesk HTTP errors (401, 403, 404, 422, 429) → status code + body text
- Network errors → exception message
Verify tool registration
After install, verify all 14 tools are registered:
ZENDESK_SUBDOMAIN=test [email protected] ZENDESK_API_TOKEN=test \
node --input-type=module <<'EOF'
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "npx",
args: ["-y", "@markusvankempen/zendesk-mcp-server"],
env: { ...process.env }
});
const client = new Client({ name: "verify", version: "1.0.0" });
await client.connect(transport);
const { tools } = await client.listTools();
console.log(`Tools registered: ${tools.length}`);
tools.forEach(t => console.log(` - ${t.name}`));
await client.close();
EOFExpected: 14 tools listed.
Troubleshooting
"Cannot find module" on startup
Ensure you have built the project: npm run build in packages/zendesk-mcp-server/. The dist/ directory must exist.
"ZENDESK_SUBDOMAIN is not set"
The server validates credentials at startup. Check that your mcp.json env block contains all three required variables.
Tool calls return 401 Unauthorized
Your ZENDESK_API_TOKEN is invalid or expired. To test:
curl -s -u "${ZENDESK_EMAIL}/token:${ZENDESK_API_TOKEN}" \
"https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets/recent.json"Tool calls return 404 for ticket IDs
The ticket ID does not exist in your Zendesk subdomain. Verify you are connecting to the correct subdomain.
SSE mode: clients connect but tool calls hang
Check that MCP_TRANSPORT=sse is set correctly. In stdio mode the server reads from stdin — there is no HTTP server. In SSE mode POST requests to /message?sessionId= must match an active session ID from a GET /sse connection.
npx is slow on first run
npx -y downloads the package on first invocation. After the npm cache is populated it starts instantly. Use a global install (npm install -g) for better cold-start performance.
Contributing
Contributions welcome! See CONTRIBUTING.md · CODE_OF_CONDUCT.md
License
MIT License · opensource.org/licenses/MIT
Documentation
| Document | Description | |----------|-------------| | Project README | Full project overview and complete documentation index | | Tools reference | All 14 MCP tools — parameters, returns, error cases, examples | | Architecture | stdio/SSE transports, auth flow, source walkthrough, CI/CD | | Python server | Python/FastMCP implementation | | VS Code extension | IDE extension setup | | Code Engine deploy | IBM Code Engine SSE deployment | | WxO deploy | watsonx Orchestrate deployment |
Topics & keywords
zendesk · zendesk-api · zendesk-mcp · zendesk-support · zendesk-tickets · mcp · mcp-server · model-context-protocol · cursor · claude · claude-desktop · github-copilot · vscode · ai-agent · ai-agents · tool-calling · function-calling · llm-tools · customer-support · customer-service · helpdesk · help-desk · service-desk · support-tickets · ticketing · crm · watsonx · watsonx-orchestrate · ibm-cloud · ibm-code-engine · typescript · fastmcp · stdio · sse · npx · automation · npm-package
Author: Markus van Kempen
Email: [email protected] · [email protected]
Website: markusvankempen.github.io
No bug too small, no syntax too weird.
