Skip to main content

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.md gives 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

  1. In Zendesk: Admin Center → Apps and integrations → APIs → Zendesk API → Add API token
  2. 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)

  1. In Zendesk: Admin Center → Apps and integrations → APIs → OAuth Clients → Add OAuth client
  2. 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
    
  3. 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.

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). Bare URLs are linked and lists get a blank line after them. Raw HTML inside Markdown is escaped; content that starts with an HTML tag is sent unchanged.

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:

  1. Regex detection — known prompt injection patterns (instruction overrides, prompt extraction, leetspeak evasion)
  2. Semantic matching — fuzzy similarity against known injection templates
  3. 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 to false to 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

Metadata

Release files for zd-cli 1.3.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zd-cli 1.3.6
File Size Uploaded Provenance
zd_cli-1.3.6.tar.gz 173.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for zd-cli 1.3.6
File Interpreter ABI Platform
zd_cli-1.3.6-py3-none-any.whl Python 3 none any Details

Total release size: 255.0 kB

Release files / zd_cli-1.3.6.tar.gz

Download URL zd_cli-1.3.6.tar.gz
Size 173.3 kB
Tags Source
SHA-256 checksum
How to use checksums
eba87ca7192445fa88246d27ec5ccfb2d0c55ba40808928c1eccbd140896b69c
BLAKE2b-256 checksum
How to use checksums
fb9be3ef96d99cd6585c1e48b0277d550cb61cdcfb8b0ce38f34a6aac64425a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / zd_cli-1.3.6-py3-none-any.whl

Download URL zd_cli-1.3.6-py3-none-any.whl
Size 81.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
88a6b117534ca60044d4ef647993d7e24178399d823086acc872a837fe22ca4a
BLAKE2b-256 checksum
How to use checksums
15fdd151143b6bd6d394d5e5d1d03d44d1f03acc8bce44c93f2f78f0714863ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for zd-cli 1.3.6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page