Zendesk MCP Server (Extended)

Zendesk MCP Server (Extended)

A production-grade MCP server for Zendesk that provides ticket management, search, user/group operations, and knowledge base access with support for local stdio and remote HTTP transports, authentication via API keys or OAuth, and multi-tenancy.

Category
访问服务器

README

Zendesk MCP Server (Extended Edition)

License version

A production-grade Model Context Protocol (MCP) server for Zendesk — run it locally with Claude Desktop over stdio, or deploy it to the cloud as a secure, multi-tenant, OAuth-protected remote MCP server that any AI application can connect to.

Note — extension of the original project

This is an extended fork of reminia/zendesk-mcp-server. The original provides a local, stdio-only Zendesk MCP server with basic ticket tools. This edition keeps full backward compatibility with it (stdio mode, original tools, prompts, and knowledge-base resource) and extends it for remote, internet-facing production use.

Issues with the original repo that this edition addresses

# Issue in original How it's addressed here
1 stdio transport only — could not be reached over a network, so it only worked on the same machine as the AI client Streamable HTTP transport (/mcp endpoint, MCP spec 2025-03-26+; SSE-as-transport is deprecated and intentionally not used) alongside stdio
2 No authentication of any kind — anyone who could reach the process could use it Three auth modes: internal scoped API keys, OAuth 2.1 resource server (generic OIDC + RFC 9728 discovery), or both simultaneously
3 No permission model — every caller could read and write tickets 4-layer read/write control: per-tool scopes (fail-closed), tool-list filtering, human-in-the-loop confirmation, dual Zendesk identity backstop
4 Blocking I/O inside async handlers — sync Zenpy calls froze the event loop under concurrent HTTP load All Zendesk calls run in worker threads (run_in_thread)
5 Deprecated offset pagination — Zendesk is sunsetting it; comments were not paginated at all (context blowout on long tickets) Cursor pagination (page[size]/page[after]) for tickets and comments
6 Sparse Zendesk coverage — only 5 tools; user/assignee IDs could not be resolved, custom fields were opaque, no search 14 tools including search, users, groups, ticket-field metadata, KB article search, attachment upload
7 Single Zendesk account hard-wired at startup from .env Multi-tenancy: per-tenant Zendesk credentials, keys/OAuth claims mapped to tenants, hot-swappable connection settings
8 No deployment story — no TLS, no health check, stdio-oriented Docker image Docker (HTTP-first, healthcheck), docker-compose with Caddy auto-TLS, Terraform for EC2 and for ECS Fargate + ALB
9 No admin tooling — key rotation/credential changes required editing .env and restarting Web admin console (separate port) + zendesk-keys CLI; connection hot-swap without restart
10 No tests 38 unit tests + HTTP/admin/OAuth smoke test suites; tests run in CI

Feature overview

Original features (retained):

  • Ticket tools: get ticket, list tickets, get comments, create ticket, update ticket, comment on ticket
  • Image attachment download with security hardening (MIME allowlist, magic-byte validation, 10 MB cap)
  • Prompts: analyze-ticket, draft-ticket-response
  • Resource: zendesk://knowledge-base (all Help Center articles, cached 1 h)
  • stdio transport for Claude Desktop / Claude Code local use

New in this edition:

  • Streamable HTTP transport with /health endpoint
  • Internal API keys: zmk_ prefix, SHA-256 hashed at rest, scopes, expiry, instant revocation, audit log
  • OAuth 2.1 resource server: JWKS/issuer/audience JWT validation, RFC 9728 Protected Resource Metadata at /.well-known/oauth-protected-resource/mcp — works with Auth0, Descope, Cognito, Keycloak, WorkOS
  • Scope model: tickets:read, tickets:write, kb:read, * — enforced per tool, fail-closed, with tools/list filtering
  • Optional elicitation: in-client approve/decline before posting public (customer-visible) comments
  • Dual Zendesk identity: reads via a restricted user (e.g. light agent), writes via a full agent
  • Multi-tenancy with per-tenant Zendesk credentials
  • Web admin console: connection settings (hot-swap + test), tenant management, key lifecycle
  • New tools: search_tickets, get_user, search_users, list_groups, list_ticket_fields, search_articles, upload_attachment
  • Tool annotations (readOnlyHint / destructiveHint) and structured output on all tools
  • Deployment: Dockerfile, docker-compose + Caddy (auto-TLS), Terraform for EC2 and ECS Fargate
  • MCP Registry manifest (server.json) and publishing guide

Tools

Tool Scope Description
get_ticket tickets:read Get a ticket by ID
get_tickets tickets:read List tickets (cursor pagination, sortable)
get_ticket_comments tickets:read Ticket comments incl. attachment metadata (cursor pagination)
search_tickets tickets:read Zendesk search syntax, e.g. status:open priority:high
get_ticket_attachment tickets:read Download an image attachment (base64, validated)
get_user tickets:read Resolve a user ID to name/email/role
search_users tickets:read Search users by name or email
list_groups tickets:read Agent groups (teams) for routing
list_ticket_fields tickets:read Field metadata — interpret/set custom_fields {id, value}
search_articles kb:read Search Help Center articles (context-safe)
create_ticket tickets:write Create a ticket
update_ticket tickets:write Update status/priority/assignee/tags/custom fields/due date
create_ticket_comment tickets:write Comment on a ticket (optional elicitation for public comments; supports attachments)
upload_attachment tickets:write Upload a file (≤10 MB), returns token for comment attachment

Prompts: analyze-ticket, draft-ticket-response. Resource: zendesk://knowledge-base (kb:read).

Quick start (local, stdio — same as the original)

git clone <this-repo>
cd zendesk-mcp-server
uv venv && uv pip install -e .
cp .env.example .env   # fill in ZENDESK_SUBDOMAIN, ZENDESK_EMAIL, ZENDESK_API_KEY

Claude Desktop config:

{
  "mcpServers": {
    "zendesk": {
      "command": "uv",
      "args": ["--directory", "/path/to/zendesk-mcp-server", "run", "zendesk"]
    }
  }
}

stdio mode is trusted-local: no auth, full access, identical behavior to the original repo.

Remote mode (Streamable HTTP)

MCP_TRANSPORT=http zendesk        # serves http://0.0.0.0:8000/mcp  +  /health

Create scoped API keys (shown once, stored hashed):

zendesk-keys create --name "reader"  --scopes tickets:read,kb:read
zendesk-keys create --name "agent"   --scopes tickets:read,tickets:write,kb:read --expires-days 30
zendesk-keys create --name "admin"   --scopes "*"
zendesk-keys list
zendesk-keys revoke --id 2

Connect a client:

claude mcp add zendesk --transport http https://mcp.example.com/mcp \
  --header "Authorization: Bearer zmk_..."

Read-only keys never see write tools in tools/list; write calls without tickets:write are denied; unknown tools are denied by default (fail closed).

Configuration reference

All configuration is via environment variables (or .env; the admin console can override connection settings at runtime, persisted in the key-store DB).

Zendesk connection

Variable Required Default Description
ZENDESK_SUBDOMAIN yes — <subdomain>.zendesk.com
ZENDESK_API_KEY yes — Zendesk API token
ZENDESK_EMAIL yes* — Single-identity mode: agent email paired with the token
ZENDESK_READ_EMAIL no ZENDESK_EMAIL Dual identity: restricted user (light agent) for all reads
ZENDESK_WRITE_EMAIL no ZENDESK_EMAIL Dual identity: full agent for all writes

*Either ZENDESK_EMAIL or both ZENDESK_READ_EMAIL/ZENDESK_WRITE_EMAIL. Zendesk roles live on the user, not the token — pairing the same token with a restricted user email yields restricted permissions (Layer 4 backstop).

Transport

Variable Default Description
MCP_TRANSPORT stdio stdio (local, trusted) or http (remote)
MCP_HOST 0.0.0.0 HTTP bind address
MCP_PORT 8000 HTTP port; MCP endpoint is /mcp

Authentication

Variable Default Description
MCP_AUTH_ENABLED true Set false only for trusted private networks
MCP_AUTH_MODE keys keys | oauth | both
MCP_KEYS_DB data/keys.db SQLite store for keys, tenants, config, audit log
MCP_PUBLIC_URL — Public base URL (required for oauth/both; used in RFC 9728 metadata)
MCP_OAUTH_ISSUER — OIDC issuer, e.g. https://your-tenant.auth0.com/
MCP_OAUTH_AUDIENCE MCP_PUBLIC_URL Audience/identifier of this server at the IdP
MCP_OAUTH_JWKS_URI <issuer>/.well-known/jwks.json Override if your IdP differs
MCP_OAUTH_AUTH_SERVERS issuer Comma-separated authorization server URLs
MCP_OAUTH_TENANT_CLAIM zendesk_tenant JWT claim naming the caller's tenant (id or name)

Admin console & safety

Variable Default Description
MCP_ADMIN_PASSWORD — (disabled) Setting it enables the admin console
MCP_ADMIN_HOST 127.0.0.1 Keep loopback; reach via SSH/SSM tunnel
MCP_ADMIN_PORT 9000 Admin console port (never expose publicly)
MCP_WRITE_CONFIRMATION false Elicit user approval before PUBLIC comments (Layer 3)

Security model

Layer Mechanism
Edge TLS 1.2+ (Caddy or ALB/ACM), security headers, 80/443 only
AuthN API keys (hashed, expiring, revocable) and/or OAuth 2.1 JWTs (PKCE at the IdP)
L1 AuthZ Central TOOL_PERMISSIONS map, enforced pre-dispatch, fail-closed
L2 Visibility tools/list filtered to caller's scopes — models can't attempt what they can't see
L3 Confirmation destructiveHint annotations + optional elicitation for public comments
L4 Zendesk Dual identity — reads through a restricted Zendesk user, writes through a full agent
Admin Separate loopback port, password + CSRF, secrets never re-displayed
Audit Append-only log: key lifecycle, admin actions, logins, writes

Multi-tenancy

By default every caller uses the server's own Zendesk connection. To let other teams/customers connect their Zendesk:

  1. Admin console → Tenants → add name, subdomain, API token, read/write emails.
  2. Bind credentials to the tenant: create an API key with that tenant selected (or zendesk-keys create ... --tenant-id N), or configure your IdP to issue the tenant's name/id in the MCP_OAUTH_TENANT_CLAIM JWT claim.
  3. All tool calls from that identity are routed to the tenant's Zendesk. Deleting a tenant revokes its keys immediately.

Web admin console

Enable with MCP_ADMIN_PASSWORD. Reach it via tunnel — never expose it:

ssh -L 9000:localhost:9000 user@host          # or the SSM equivalent, see docs/DEPLOYMENT.md
# open http://localhost:9000

Provides: Zendesk connection editor with hot-swap (no restart) and "test connection", tenant management, API key create/revoke with scope checkboxes and expiry, and key usage visibility. Stored tokens are never re-displayed; new keys are shown exactly once.

Deployment

Docker (single container):

docker build -t zendesk-mcp-server .
docker run --rm -p 8000:8000 --env-file .env -v zmcp-keys:/data zendesk-mcp-server

Docker Compose + automatic TLS (recommended single-box): set MCP_DOMAIN in .env, then docker compose up -d — Caddy terminates TLS with Let's Encrypt and proxies to the server. Create keys with docker compose exec zendesk-mcp zendesk-keys create ....

AWS EC2 (Terraform): terraform/ provisions EC2 (SSM access, no SSH, IMDSv2), security group (80/443 only), Elastic IP, optional Route53, and bootstraps Docker + the repo. Full runbook: docs/DEPLOYMENT.md.

Windows Server without Docker: native Python + Caddy with your own TLS certificate + NSSM services, including a troubleshooting FAQ of real-world Windows issues (Node/PATH, cert chains, arg mangling): docs/WINDOWS_SETUP.md.

AWS ECS Fargate + ALB (scale-out): terraform/ecs/ provisions ECR, Fargate service, ALB with ACM/TLS 1.3, EFS-backed key store, Secrets Manager injection, and CloudWatch. Keep desired_count=1 until the key store is migrated off SQLite.

MCP Registry: fill in server.json and follow docs/REGISTRY.md to publish your deployed server to registry.modelcontextprotocol.io.

Development

uv sync --extra dev
uv run pytest tests/ -v        # 38 tests: tools, keystore, permissions, tenancy, auth modes

Project layout:

src/zendesk_mcp_server/
  server.py          # FastMCP app: tools, prompts, resources, transports
  zendesk_client.py  # Zendesk API client (dual identity, cursor pagination)
  auth.py            # API-key verifier, OIDC JWT verifier, auth-mode factory
  permissions.py     # Scope model + enforcement/filtering middleware
  keystore.py        # SQLite: keys, tenants, audit log
  runtime.py         # Client holder: hot-swap + per-tenant routing
  admin.py           # Web admin console (separate port)
  keys_cli.py        # zendesk-keys CLI
  config.py          # Env-based settings
terraform/           # EC2 deployment     terraform/ecs/  # Fargate deployment
docs/                # Architecture plan, deployment runbook, registry guide

Architecture decisions and full history: docs/REMOTE_MCP_ARCHITECTURE_PLAN.md.

License

Apache 2.0 — same as the original project. Original work by reminia; extensions as described above.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选