使用说明
Zendesk CLI, MCP Server & Claude Code Skill
A Claude Code skill for Zendesk Support integration. The primary interface is a skill file (SKILL.md) that gives Claude full command knowledge — backed by a CLI (zd-cli) that Claude runs to interact with Zendesk. An MCP server is also included for other AI assistants.
This project was built almost entirely by Claude Code through iterative conversation — designing, implementing, testing against a live Zendesk instance, and fixing issues together over multiple sessions. It's a tool I use often and am sharing in case others find it useful.
Features
- Claude Code skill —
SKILL.mdgives Claude full command documentation, workflows, and the save-first/query-later pattern - CLI (
zd-cli) — 28 commands covering tickets, users, organizations, views, metrics, and more - MCP server — alternative interface for other AI assistants
- Slack integration — send formatted support reports to Slack channels
- Support metrics analysis — response times, resolution rates, CSAT, after-hours and on-call tracking
- Local response storage — API responses saved to temp directory, queryable with jq without re-fetching
- Markdown formatting — write operations convert Markdown to HTML for proper rendering in Agent Workspace
- Encrypted credentials — API tokens and OAuth tokens encrypted at rest
Quick Start
# Run directly (no install needed)
uvx zd-cli --help
# Set up credentials (see Authentication below)
uvx zd-cli auth login
# Verify it works
uvx zd-cli me
Authentication
Three methods are supported. API token is the simplest; OAuth adds auto-refresh. A relay server mode exists for shared team deployments but is not covered here.
Method 1: API Token
- In Zendesk: Admin Center → Apps and integrations → APIs → Zendesk API → Add API token
- Copy the token (shown only once)
# Interactive setup (prompts for email, token, subdomain)
zd-cli auth login
# Or non-interactive
zd-cli auth login --email you@company.com --token YOUR_TOKEN --subdomain yourcompany
# Or via environment variables
export ZENDESK_EMAIL="you@company.com"
export ZENDESK_TOKEN="your-api-token"
export ZENDESK_SUBDOMAIN="yourcompany"
Method 2: OAuth 2.0 (with auto-refresh)
- In Zendesk: Admin Center → Apps and integrations → APIs → OAuth Clients → Add OAuth client
- Add redirect URIs for ports 8080–8089:
http://127.0.0.1:8080/callback http://127.0.0.1:8081/callback ... http://127.0.0.1:8089/callback - Note the Client ID and Client Secret
# Opens a browser for authorization
zd-cli auth login-oauth --subdomain yourcompany --client-id YOUR_ID --client-secret YOUR_SECRET
# Headless/SSH: browser redirects to localhost (shows connection refused — that's expected),
# paste the full redirect URL back into the prompt
zd-cli auth login-oauth --subdomain yourcompany --client-id YOUR_ID --client-secret YOUR_SECRET --manual
# Check auth status
zd-cli auth status
# Remove credentials
zd-cli auth logout # API token
zd-cli auth logout-oauth # OAuth token
Installation
Prerequisites: Python 3.12+, uv, and jq (for the query command). On macOS, brew install uv jq installs both.
Via uvx (no install, recommended)
uvx zd-cli --help
uvx runs the tool in an isolated environment on demand — nothing is permanently installed.
Via uv tool install
uv tool install zd-cli
zd-cli --help
uv tool upgrade zd-cli # later, to update
Development / Claude Code skill
git clone https://github.com/andmarios/zendesk-skill zendesk-skill
cd zendesk-skill
uv sync
uv run zd-cli --help
Point Claude Code at SKILL.md to give Claude full command documentation and workflows.
Commands
Tickets
| Command | Description |
|---|---|
search "status:open priority:urgent" |
Search tickets |
ticket 12345 |
Get ticket by ID |
ticket-details 12345 |
Ticket + all comments |
linked-incidents 12345 |
Incidents linked to a problem ticket |
attachment --ticket 12345 <url> |
Download an attachment |
Write Operations
All write commands convert Markdown to HTML by default (use --plain-text to skip).
| Command | Description |
|---|---|
update-ticket 12345 --status pending --tags "waiting-customer" |
Update ticket |
create-ticket "Subject" "**Bold** description" |
Create ticket |
add-note 12345 "Internal **note**" |
Add internal note |
add-comment 12345 "Public reply" |
Add public comment |
Metrics & Analytics
| Command | Description |
|---|---|
ticket-metrics 12345 |
Reply/resolution times for a ticket |
list-metrics |
Metrics across tickets |
satisfaction-ratings --score bad |
CSAT ratings |
Views
| Command | Description |
|---|---|
views |
List available views |
view-count 123 |
Ticket count in a view |
view-tickets 123 |
Tickets from a view |
Users & Organizations
| Command | Description |
|---|---|
user 12345 |
Get user by ID |
search-users "john@example.com" |
Search users |
org 67890 |
Get organization by ID |
search-orgs "Acme" |
Search organizations |
Configuration & Info
| Command | Description |
|---|---|
me |
Current user (tests auth) |
groups |
List support groups |
tags |
Popular tags |
sla-policies |
SLA policies |
Query
# Query a saved response file with jq
zd-cli query <file> -q comments_slim # named query
zd-cli query <file> --jq '.data.ticket' # custom jq
zd-cli query <file> --list # show available named queries
All commands save their full API response to /tmp/zd-cli-$UID/ and print the path. Use zd-cli query to extract data from saved files without re-fetching.
Search Query Syntax
# Status & priority
zd-cli search "status:open priority:urgent"
zd-cli search "status:pending assignee:me"
# Time filters
zd-cli search "created>2024-01-01 status:open"
zd-cli search "updated<1week status:open"
# By person/org
zd-cli search "requester:user@example.com status:open"
zd-cli search "organization:acme type:incident"
# Tags
zd-cli search "tags:billing tags:escalated"
Slack Integration
# Configure
zd-cli auth login-slack --webhook "https://hooks.slack.com/services/..." --channel "#support"
# Send a report
zd-cli slack-report
# Check / remove
zd-cli auth status-slack
zd-cli auth logout-slack
Support Metrics Analysis
# Default: last 2 weeks
uv run python src/zendesk_skill/scripts/analyze_support_metrics.py
# Custom range with accurate reply counts from the Ticket Metrics API
uv run python src/zendesk_skill/scripts/analyze_support_metrics.py \
--start 2026-01-01 --end 2026-01-15 --fetch-metrics
# Pipe to Slack
uv run python src/zendesk_skill/scripts/analyze_support_metrics.py | zd-cli slack-report
# Generate markdown report
zd-cli markdown-report -o report.md
Business Hours & On-Call Tracking
Configure in ~/.config/zd-cli/config.json to add after-hours and on-call sections to your reports:
{
"business_hours": {
"timezone": "Europe/Berlin",
"start_hour": 9,
"end_hour": 18,
"workdays": [0, 1, 2, 3, 4]
},
"oncall": {
"enabled": true,
"start_hour": 19,
"end_hour": 9,
"customers": ["bigclient.com"],
"priorities": ["urgent"]
}
}
When configured, reports include after-hours ticket counts, messages, replies, and on-call engagements (tickets matching the configured priority + customer domain during on-call hours). FRT for on-call tickets is calculated in calendar time; all others use business hours.
Security: Prompt Injection Protection
All content from Zendesk is treated as untrusted input and screened through a multi-layer security pipeline before reaching the LLM:
- Regex detection — known prompt injection patterns (instruction overrides, prompt extraction, leetspeak evasion)
- Semantic matching — fuzzy similarity against known injection templates
- Haiku/LLM screening — when configured, an LLM-based classifier for sophisticated attacks
Every LLM-facing field (ticket subjects, comment bodies, user names, emails, org names, view titles) is wrapped with session-scoped security markers. Stored response files are also scanned at save time using both regex and semantic screening tiers, with detection results preserved in file metadata.
Attachment scanning is size-aware:
- Text files up to 1 MB are scanned inline through the full screening pipeline
- Text files over 1 MB and binary files receive a security warning with a hint to scan manually using the CLI tool:
uvx prompt-security-utils <file>
Security is enabled by default. Configure in ~/.config/zd-cli/config.json:
{
"security_enabled": true,
"allowlisted_tickets": ["12345", "67890"]
}
security_enabled— set tofalseto disable all screening and wrapping (default:true)allowlisted_tickets— ticket IDs to skip wrapping (for trusted/internal tickets)
# View current security status and session markers
zd-cli security-info
# Include full MCP security instructions
zd-cli security-info --instructions
Requires prompt-security-utils (included as a dependency).
Local file permissions
Saved responses, exports and downloaded attachments contain customer data, so they are written owner-only: directories 0700, files 0600. The tool refuses to use /tmp/zd-cli-$UID/ if it is a symlink or owned by another user, and it never changes permissions on anything outside that directory. If a directory cannot be secured the command fails with a File system error message; if only a file's mode cannot be tightened, a warning is printed on stderr.
MCP Server
The MCP server exposes the same functionality for AI assistants that support the Model Context Protocol.
uv run zendesk-mcp
Add to claude_desktop_config.json:
{
"mcpServers": {
"zendesk": {
"command": "uvx",
"args": ["--from", "zendesk-skill", "zendesk-mcp"]
}
}
}
Development
uv run pytest -v
uv run zd-cli --help
License
MIT
