A Model Context Protocol server that exposes Zendesk ticket read and write tools to Claude Code and other MCP clients.
- Search, list (paginated), and fetch Zendesk tickets, comments, and attachments
- Create new tickets and update existing ticket fields (including group, custom status, and tags)
- Post public replies and internal notes
- Set ticket status and assign tickets to agents
- Browse and apply views and macros
- Look up users, groups, organizations, and custom statuses
- Read and write time-tracking entries
- Format a ticket as a Markdown issue draft for handoff to a tracker (GitLab, GitHub, Jira)
- Two MCP prompts (
analyze-ticket,draft-ticket-response) for ticket analysis and response drafting - (Optional) Expose Zendesk Help Center articles as an MCP resource
- (Optional) Read linked GitLab issues / MRs / commits via the Git-Zen Zendesk app
- Python 3.10 or newer
- A Zendesk OAuth client. A Zendesk admin can create one at:
https://<your-subdomain>.zendesk.com/admin/apps-integrations/apis/zendesk-api/oauth_clientsSet the redirect URL tohttp://localhost:8787/callbackand request scopesread write.
Install into a project-local virtualenv. Using a venv keeps zendesk-mcp and its dependencies isolated from your system Python and from other projects, and is the recommended path for everything below.
From a clone of this repository:
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e .For development (also installs pytest):
.venv/bin/pip install -e ".[dev]"Throughout this README, commands use the venv's binaries via
.venv/bin/.... You can insteadsource .venv/bin/activateonce per shell and drop the prefix — the result is the same.
Run the interactive setup using the venv's Python:
.venv/bin/python -m zendesk_mcp setupYou will be prompted for:
- Your Zendesk subdomain (e.g.
acmeforacme.zendesk.com) - The OAuth client ID created by your admin
- The OAuth client secret
- (Optional) A Git-Zen integration field ID — see Optional: Git-Zen integration
- (Optional) Whether to enable the Help Center knowledge base resource — see Optional: Help Center knowledge base
The setup opens a browser for the OAuth authorization step, then writes a token to ~/.config/zendesk-mcp/config.json (mode 0600).
If you have no browser, the URL is printed to the terminal — open it on any device, click Allow, and paste the resulting redirect URL back into the prompt.
Zendesk access tokens expire. OAuth clients created on or after 2026-04-30 get a 30-minute default lifetime; older clients issue non-expiring tokens unless an expiry is requested. Setup requests a 24-hour access token and a 90-day refresh token so the behaviour is the same either way, and the server renews the access token automatically — before it expires, and again if Zendesk rejects a token mid-request.
To make that possible, the config file also stores refresh_token, expires_at,
client_id, and client_secret alongside the access token. Keep the file at mode 0600;
it is the same trust level as the access token itself. If your OAuth client returns no
refresh token, setup says so and the token is used as-is.
Re-run .venv/bin/python -m zendesk_mcp setup when:
- the refresh token expires (90 days with no use), or
- you revoke the OAuth grant in Zendesk.
In either case the tools return Zendesk authorization failed: ... Re-run: zendesk-mcp setup
rather than failing opaquely.
Register the MCP server using the venv's Python by absolute path. Claude Code launches the server in a fresh shell that does not inherit your activated venv, so the absolute path is required — pointing at a bare python here will fail to import zendesk_mcp.
ZENDESK_MCP_DIR="$(pwd)" # run this from the repo root, after install
claude mcp add --scope user zendesk -- "$ZENDESK_MCP_DIR/.venv/bin/python" -m zendesk_mcpOr just inline the absolute path you want:
claude mcp add --scope user zendesk -- /absolute/path/to/zendesk-mcp/.venv/bin/python -m zendesk_mcpThen add the read tools to permissions.allow in ~/.claude/settings.json to avoid per-call prompts:
{
"permissions": {
"allow": [
"mcp__zendesk__zendesk_get_ticket",
"mcp__zendesk__zendesk_get_tickets",
"mcp__zendesk__zendesk_get_comments",
"mcp__zendesk__zendesk_list_attachments",
"mcp__zendesk__zendesk_download_attachment",
"mcp__zendesk__zendesk_search_tickets",
"mcp__zendesk__zendesk_ticket_to_gitlab_context",
"mcp__zendesk__zendesk_list_views",
"mcp__zendesk__zendesk_get_view",
"mcp__zendesk__zendesk_get_view_tickets",
"mcp__zendesk__zendesk_list_macros",
"mcp__zendesk__zendesk_preview_macro",
"mcp__zendesk__zendesk_search_users",
"mcp__zendesk__zendesk_get_groups",
"mcp__zendesk__zendesk_get_group_users",
"mcp__zendesk__zendesk_get_organization",
"mcp__zendesk__zendesk_list_custom_statuses"
]
}
}Write tools (zendesk_post_comment, zendesk_post_internal_note, zendesk_set_ticket_status, zendesk_assign_ticket, zendesk_create_ticket, zendesk_update_ticket, zendesk_log_time, zendesk_add_tag, zendesk_remove_tag, zendesk_apply_macro) are intentionally not in the default allow-list — Claude will prompt you per call.
| Tool | What it does |
|---|---|
zendesk_search_tickets |
Search tickets by status, priority, type, assignee, requester, tags, or keyword |
zendesk_get_tickets |
List tickets with pagination and sorting (page, per_page, sort_by, sort_order) |
zendesk_get_ticket |
Get one ticket's metadata |
zendesk_create_ticket |
Create a new ticket (subject, description, optional priority/type/assignee_id/requester_id/tags/custom_fields) |
zendesk_update_ticket |
Update one or more fields on an existing ticket (status, priority, subject, type, assignee_id, requester_id, group_id, custom_status_id, tags, custom_fields, due_at) |
zendesk_get_comments |
Get the conversation thread on a ticket |
zendesk_list_attachments |
List attachments on a ticket |
zendesk_download_attachment |
Download an attachment to a local cache directory |
zendesk_ticket_to_gitlab_context |
Format a ticket and its conversation as a Markdown issue draft |
zendesk_post_comment |
Post a public reply on a ticket |
zendesk_post_internal_note |
Post an agent-only internal note on a ticket |
zendesk_set_ticket_status |
Set ticket status (new, open, pending, hold, solved, closed) |
zendesk_assign_ticket |
Assign a ticket to an agent by email or me |
| Tool | What it does |
|---|---|
zendesk_add_tag |
Add a tag to a ticket (idempotent) |
zendesk_remove_tag |
Remove a tag from a ticket (idempotent) |
| Tool | What it does |
|---|---|
zendesk_list_views |
List all active views |
zendesk_get_view |
Get a view's filter conditions and execution settings |
zendesk_get_view_tickets |
Fetch tickets currently matching a view |
zendesk_list_macros |
List active macros with their actions |
zendesk_preview_macro |
Preview what changes a macro would make |
zendesk_apply_macro |
Apply a macro to a ticket (applies field changes and posts any comment) |
| Tool | What it does |
|---|---|
zendesk_search_users |
Find users by name or email |
zendesk_get_groups |
List all active groups |
zendesk_get_group_users |
List the members of a group |
zendesk_get_organization |
Fetch an organization including custom fields |
zendesk_list_custom_statuses |
List all custom ticket statuses and their IDs |
| Tool | What it does |
|---|---|
zendesk_get_time_tracking |
Read time-tracking entries for a ticket |
zendesk_log_time |
Log a time entry against a ticket |
| Tool | What it does |
|---|---|
zendesk_get_git_zen_links |
(Git-Zen only) Get linked GitLab issues / MRs / commits for a ticket |
The server exposes two MCP prompts that some clients (e.g. Claude Desktop) surface as slash commands:
| Prompt | Argument | What it does |
|---|---|---|
analyze-ticket |
ticket_id |
Asks the model to fetch the ticket and produce a summary, status/timeline, and key interaction points |
draft-ticket-response |
ticket_id |
Asks the model to fetch the ticket and draft a customer-facing response (with a confirmation step before posting) |
If your Zendesk instance uses the Git-Zen app, the zendesk_get_git_zen_links tool can read its custom-field payload. Find your instance's Git-Zen custom field ID under Admin → Tickets → Fields (it is a numeric ID), then either set it during .venv/bin/python -m zendesk_mcp setup or edit ~/.config/zendesk-mcp/config.json to add:
{
"git_zen_field_id": 12345678901234
}Without this configured, zendesk_get_git_zen_links returns a "not configured" message.
If your Zendesk instance has a published Help Center, you can expose its sections and articles as the zendesk://knowledge-base MCP resource. The resource returns a single JSON document covering all sections and articles, cached for one hour.
This is opt-in. Enable it by either answering "y" to the prompt during .venv/bin/python -m zendesk_mcp setup, or by adding the following to ~/.config/zendesk-mcp/config.json:
{
"knowledge_base_enabled": true
}When the flag is absent or false, the resource is not registered, keeping the server's resource list empty for instances without a Help Center.
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytestTests run on Python 3.10, 3.11, and 3.12 in CI (see .github/workflows/test.yml).
Bump the version in pyproject.toml, mcpb/pyproject.toml (both the version and
the zendesk-mcp== pin), mcpb/manifest.json, and server.json (both fields),
then push a v* tag. That triggers .github/workflows/release.yml, which publishes
to PyPI, packs the MCPB bundle, publishes server.json to the MCP Registry, and
cuts the GitHub release.
Check the versions agree before tagging — the release fails fast otherwise:
python3 .github/scripts/check_versions.py 0.1.5