@sshadows/zendesk-mcp-server
v1.3.0
Published
MCP Server for Zendesk API with enhanced features
Maintainers
Readme
Zendesk MCP Server
A Model Context Protocol server for Zendesk API integration with AI-powered ticket analysis
Quick Start • Configuration • Tools • Architecture • Development
Overview
Zendesk MCP Server provides comprehensive access to the Zendesk API through the Model Context Protocol. It supports two transport modes that are auto-detected from environment variables:
- Stdio mode — API token auth, for CLI-based MCP clients (Claude Code, Cursor, etc.)
- HTTP mode — OAuth 2.1 with PKCE, for web-based MCP clients
Both modes expose the same set of tools. No code changes are needed to switch between them.
Based on mattcoatsworth/zendesk-mcp-server with significant enhancements including AI-powered features, dual-mode authentication, improved error handling, and comprehensive retry logic.
Quick Start
Stdio Mode (API Token)
Best for CLI-based MCP clients like Claude Code or Cursor. Each user only needs their own email + API token.
1. Get your API token from Zendesk Admin Center → Apps and integrations → APIs → Zendesk API → Add API token.
2. Configure your MCP client:
{
"zendesk": {
"type": "stdio",
"command": "npx",
"args": ["@sshadows/zendesk-mcp-server"],
"env": {
"ZENDESK_SUBDOMAIN": "your-subdomain",
"ZENDESK_EMAIL": "[email protected]",
"ZENDESK_API_TOKEN": "your-api-token"
}
}
}That's it. The server auto-detects stdio mode and connects.
HTTP Mode (OAuth 2.1)
Best for web-based MCP clients or multi-user deployments with centralized OAuth.
1. Create an OAuth app in Zendesk Admin Center → Apps and integrations → APIs → OAuth Clients.
2. Create a .env file:
ZENDESK_SUBDOMAIN=your-subdomain
ZENDESK_OAUTH_CLIENT_ID=your_client_id
ZENDESK_OAUTH_CLIENT_SECRET=your_client_secret
ZENDESK_OAUTH_REDIRECT_URI=http://localhost:3030/zendesk/oauth/callback3. Start and authorize:
npm start
# Visit http://localhost:3030/oauth/authorize in your browser4. Use the token with your MCP client: Authorization: Bearer mcp_...
Installation
| Requirement | Version | Notes | |------------|---------|-------| | Node.js | >= 18.0.0 | Required | | Zendesk Account | Any plan | Required | | Anthropic API Key | - | Only for AI analysis features |
# npm (recommended)
npm install -g @sshadows/zendesk-mcp-server
# Or from source
git clone https://github.com/SShadowS/zendesk-mcp-server.git
cd zendesk-mcp-server
npm installConfiguration
Environment Variables
The server auto-detects which mode to use based on which variables are set.
Stdio mode (set ZENDESK_EMAIL + ZENDESK_API_TOKEN, without ZENDESK_OAUTH_CLIENT_ID):
ZENDESK_SUBDOMAIN=mycompany
[email protected]
ZENDESK_API_TOKEN=your-api-tokenHTTP mode (set ZENDESK_OAUTH_CLIENT_ID):
ZENDESK_SUBDOMAIN=mycompany
ZENDESK_OAUTH_CLIENT_ID=your_client_id
ZENDESK_OAUTH_CLIENT_SECRET=your_client_secret
ZENDESK_OAUTH_REDIRECT_URI=http://localhost:3030/zendesk/oauth/callbackCommon (both modes):
MODE=full # 'full' (all 55 tools) or 'lite' (10 essential tools)
ANTHROPIC_API_KEY=sk-ant-... # Required for AI image/document analysis
ZENDESK_DEBUG=false # Enable debug loggingHTTP mode only:
PORT=3030
SERVER_BASE_URL=http://localhost:3030 # Use https:// in productionSee .env.example for the full list.
Tool Modes
Control which tools are exposed with the MODE environment variable:
full(default) — All 55 tools available.lite— 10 essential tools for reduced context usage:search,get_user,list_tickets,get_ticket,get_ticket_comments,add_ticket_comment,get_ticket_attachments,analyze_ticket_images,analyze_ticket_documents,get_document_summary.
MODE=lite npm startAvailable Tools
| Tool | Description |
|------|-------------|
| list_tickets | List tickets with filters (status, assignee, etc.) |
| get_ticket | Get ticket details with optional comments |
| create_ticket | Create a new ticket |
| update_ticket | Update ticket fields |
| get_ticket_comments | Get all comments on a ticket |
| add_ticket_comment | Add public or internal comment |
| get_ticket_attachments | Get ticket attachments |
| analyze_ticket_images | AI-powered image analysis with Claude Vision |
| analyze_ticket_documents | AI-powered document analysis |
| get_document_summary | Quick document summary |
| Tool | Description |
|------|-------------|
| list_users | List all users |
| get_user | Get user details |
| create_user | Create new user |
| update_user | Update user info |
| delete_user | Delete a user |
| Tool | Description |
|------|-------------|
| list_organizations | List all organizations |
| get_organization | Get organization details |
| create_organization | Create new organization |
| update_organization | Update organization |
| delete_organization | Delete organization |
| Category | Tools |
|----------|-------|
| Groups | list_groups, get_group, create_group, update_group, delete_group |
| Macros | list_macros, get_macro, create_macro, update_macro, delete_macro |
| Views | list_views, get_view, create_view, update_view, delete_view |
| Triggers | list_triggers, get_trigger, create_trigger, update_trigger, delete_trigger |
| Automations | list_automations, get_automation, create_automation, update_automation, delete_automation |
| Category | Tools |
|----------|-------|
| Search | search — Search across all Zendesk data |
| Help Center | list_articles, get_article, create_article, update_article, delete_article |
| Talk | get_talk_stats — Phone support statistics |
| Chat | list_chats — Chat conversations |
Architecture
Transport Modes
src/index.js (auto-detection)
├── ZENDESK_EMAIL + ZENDESK_API_TOKEN → Stdio mode
│ ├── ZendeskClient.setApiTokenAuth()
│ ├── setDefaultZendeskClient(client)
│ └── StdioServerTransport (stdin/stdout)
│
└── ZENDESK_OAUTH_CLIENT_ID → HTTP mode
├── Express server (src/http-server.js)
├── OAuth 2.1 with PKCE (src/auth/)
├── Per-session ZendeskClient instances
└── StreamableHTTPServerTransportTools are identical in both modes. They call getZendeskClient() which resolves to:
- HTTP mode: Per-session client via AsyncLocalStorage
- Stdio mode: Singleton default client
Project Structure
zendesk-mcp-server/
├── src/
│ ├── index.js # Entry point (auto-detects mode)
│ ├── http-server.js # Express server with OAuth (HTTP mode only)
│ ├── server.js # MCP server setup and tool registration
│ ├── request-context.js # Per-session + default client context
│ ├── auth/
│ │ ├── oauth-handler.js # OAuth 2.1 with PKCE
│ │ ├── session-store.js # Session management
│ │ └── middleware.js # Bearer token auth middleware
│ ├── zendesk-client/
│ │ ├── base.js # Auth, HTTP requests, retry logic
│ │ ├── index.js # Mixin composition
│ │ ├── tickets.js # Ticket API methods
│ │ ├── users.js # User API methods
│ │ └── ... # Other API domain mixins
│ ├── tools/ # MCP tool implementations
│ ├── config/
│ │ └── tool-modes.js # Full/lite mode filtering
│ └── utils/
│ ├── errors.js # Classified error types
│ ├── retry.js # Exponential backoff
│ ├── ticket-context.js # AI prompt context builder
│ ├── document-handler.js # Document routing
│ └── converter-client.js # Office-to-PDF conversion
├── tests/ # Vitest test suite
├── .env.example # Environment variable template
└── CLAUDE.md # AI assistant project guideKey Design Decisions
- Dual auth in one client:
ZendeskClientBasesupports bothsetApiTokenAuth()(Basic) andsetAccessToken()(Bearer). The_authModefield determines which headergetAuthHeader()returns. - Default client fallback: AsyncLocalStorage doesn't propagate through StdioServerTransport's event callbacks. Instead of fighting that,
getZendeskClient()falls back to a module-level default client in stdio mode. Zero changes needed in any tool file. - Console.error everywhere: In stdio mode, stdout is the MCP transport. All diagnostic logging in shared code paths uses
console.error. - HTTP mode is unchanged:
src/http-server.jsandsrc/auth/*are only imported in HTTP mode. No changes were needed.
Development
npm start # Start server (auto-detects mode)
npm run dev # Start with auto-reload
npm test # Run all tests
npm run test:watch # Run tests in watch mode
npm run inspect # Launch MCP InspectorTesting
Tests use Vitest and are in tests/ mirroring the src/ directory:
npm test # Run all tests
npm run test:watch # Watch modeIntegration tests (against real Zendesk + Anthropic APIs) require .env credentials and are automatically skipped when credentials are missing.
HTTP Mode Endpoints
| Endpoint | Description |
|----------|-------------|
| ALL /mcp | Main MCP endpoint (requires Bearer token) |
| GET /oauth/authorize | Start OAuth flow |
| GET /zendesk/oauth/callback | OAuth callback |
| POST /oauth/token | Token exchange |
| POST /oauth/register | Dynamic client registration (RFC 7591) |
| GET /.well-known/oauth-authorization-server | OAuth metadata (RFC 8414) |
| GET /.well-known/oauth-protected-resource | Protected resource metadata (RFC 9728) |
| GET /health | Health check |
Troubleshooting
The server needs either API token or OAuth credentials. Set one of:
# Stdio mode
ZENDESK_SUBDOMAIN=... ZENDESK_EMAIL=... ZENDESK_API_TOKEN=...
# HTTP mode
ZENDESK_SUBDOMAIN=... ZENDESK_OAUTH_CLIENT_ID=... ZENDESK_OAUTH_CLIENT_SECRET=...- Complete OAuth flow: visit
http://localhost:3030/oauth/authorize - Check if token expired (24-hour TTL) — re-authorize if needed
- Ensure Bearer token is included:
Authorization: Bearer mcp_xxx - Token format should start with
mcp_
- Verify
ZENDESK_EMAILis correct - Verify
ZENDESK_API_TOKENis a valid API token (not a password) - Verify
ZENDESK_SUBDOMAINis correct - Check the connection test output in stderr on startup
In-memory sessions are cleared on restart. Re-authorize to get a new token. For production, implement a Redis-based session store (see src/auth/session-store.js).
Set ANTHROPIC_API_KEY in your environment. This is only needed for analyze_ticket_images, analyze_ticket_documents, and get_document_summary.
The server includes exponential backoff retry logic. If you hit rate limits frequently, consider using MODE=lite to reduce API calls, or check if multiple clients share the same credentials.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature) - Run tests (
npm test) - Commit your changes
- Open a Pull Request
License
MIT License — see LICENSE for details.
Acknowledgments
- Original implementation by @mattcoatsworth
- Built with Model Context Protocol
- AI features powered by Anthropic Claude
Made with care by SShadowS
