zendesk-mcp
README.md
# zendesk-mcp
Dedalus MCP server for Zendesk support workflows: ticket search, ticket lookup, user listing, ticket creation, internal notes or public comments, and ticket status updates.
## Tools
- `search_tickets` - Search Zendesk tickets with Zendesk search syntax.
- `get_ticket` - Fetch one ticket by ID.
- `list_users` - List Zendesk users, optionally filtered by role.
- `create_ticket` - Create a new ticket with an initial public comment.
- `add_ticket_comment` - Add an internal note or customer-visible public comment.
- `update_ticket_status` - Change a ticket status.
## Safety Notes
`create_ticket`, `add_ticket_comment`, and `update_ticket_status` are write tools. Zendesk triggers, automations, and notification settings may email customers or alter support queues. `add_ticket_comment` defaults to `public_comment=false`; set it to `true` only when a customer-visible reply is intended.
Do not put Zendesk OAuth access tokens, API tokens, OAuth client secrets, requester personal data, ticket bodies, or customer emails in commits, logs, issue trackers, screenshots, or public demos.
## Authentication Model
This server is built for Dedalus DAuth with OAuth-style bearer tokens. The connection name is `zendesk-mcp`, the secret key is `ZENDESK_ACCESS_TOKEN`, and requests use:
```text
Authorization: Bearer {api_key}
```
For a multi-user marketplace deployment, the user-specific values are:
- `ZENDESK_BASE_URL`: the user's Zendesk account URL, for example `https://none-83105.zendesk.com`.
- `ZENDESK_ACCESS_TOKEN`: an OAuth access token for that Zendesk account and user.
Zendesk OAuth endpoints are Zendesk-account scoped. A static Dedalus OAuth deployment with fixed `OAUTH_AUTHORIZE_URL` and `OAUTH_TOKEN_URL` only covers that one Zendesk subdomain. To support unrelated customers' own Zendesk accounts, Dedalus must receive each user's Zendesk base URL as connection/provider metadata, and the OAuth authorization must be performed against that same account.
The server code intentionally does not hardcode a Zendesk subdomain. `ZENDESK_BASE_URL` is only a local-smoke-test or intentional single-tenant fallback.
## Dedalus Deployment
Connection settings:
```text
Connection name: zendesk-mcp
Auth type: OAuth
Header Name: Authorization
Header Format: Bearer {api_key}
```
Required credentials for each end user:
```text
ZENDESK_BASE_URL Example: https://none-83105.zendesk.com
ZENDESK_ACCESS_TOKEN Example: OAuth access token for that Zendesk account
```
If the Dedalus dashboard cannot supply per-user `base_url`/provider metadata to the connection, set `ZENDESK_BASE_URL` as an environment variable and treat the deployment as single-tenant for one Zendesk account.
Environment variables for a normal Dedalus deployment:
```bash
DEDALUS_AS_URL=https://as.dedaluslabs.ai
PORT=8080
```
Optional single-tenant Dedalus-managed OAuth variables:
```bash
OAUTH_ENABLED=true
OAUTH_AUTHORIZE_URL=https://<subdomain>.zendesk.com/oauth/authorizations/new
OAUTH_TOKEN_URL=https://<subdomain>.zendesk.com/oauth/tokens
OAUTH_CLIENT_ID=<zendesk-oauth-client-id>
OAUTH_CLIENT_SECRET=<zendesk-oauth-client-secret>
OAUTH_SCOPES_AVAILABLE=read,write
ZENDESK_BASE_URL=https://<subdomain>.zendesk.com
```
Do not set `ZENDESK_ACCESS_TOKEN` as a global deployment environment variable for a shared marketplace server. It must be supplied per user through DAuth credentials or OAuth.
## Local OAuth Smoke Testing
Create a Zendesk OAuth client in the target Zendesk Admin Center and add this redirect URL:
```text
http://127.0.0.1:8765/callback
```
Local `.env` minimum:
```bash
ZENDESK_BASE_URL=https://<subdomain>.zendesk.com
OAUTH_CLIENT_ID=<zendesk-oauth-client-id>
OAUTH_CLIENT_SECRET=<zendesk-oauth-client-secret>
OAUTH_SCOPES_AVAILABLE=read,write
ZENDESK_OAUTH_REDIRECT_URI=http://127.0.0.1:8765/callback
```
Then run:
```bash
uv run python src/oauth_local.py --write-env
```
The helper derives `OAUTH_AUTHORIZE_URL` and `OAUTH_TOKEN_URL` from `ZENDESK_BASE_URL` when they are not explicitly set, opens the browser authorization page, captures the localhost callback, exchanges the code, and writes `ZENDESK_ACCESS_TOKEN` to `.env`.
## Run Locally
```bash
uv run python src/main.py
```
In another terminal:
```bash
MCP_SERVER_URL=http://127.0.0.1:8080/mcp uv run python src/_client.py
```
By default, `_client.py` uses `ZENDESK_LOCAL_TOOL_CALLS=1`, which bypasses the Dedalus MCP HTTP auth layer and calls the same tool functions through a local dispatch context. This validates the tool logic against the real Zendesk API without requiring a Dedalus-issued MCP Authorization header.
The first `_client.py` check is `auth_check`, which calls `/api/v2/users/me.json` and prints only masked identity data. A successful login as an `end-user` is not enough for the full tool suite; search, user listing, ticket creation, comments, and status updates normally require an agent or admin account.
By default, keep `ZENDESK_RUN_WRITES=0` to avoid creating or updating tickets. Set `ZENDESK_RUN_WRITES=1` only when you intentionally want `_client.py` to call `create_ticket`, `add_ticket_comment`, and `update_ticket_status`.
## Deployed Smoke Test
For an SDK-based deployed-server smoke test, `.env` needs:
```bash
DEDALUS_API_KEY=dsk-live-...
DEDALUS_API_URL=https://api.dedaluslabs.ai
DEDALUS_AS_URL=https://as.dedaluslabs.ai
DEDALUS_MCP_SERVER=dedalus-labs/zendesk-mcp
ZENDESK_BASE_URL=https://<subdomain>.zendesk.com
ZENDESK_ACCESS_TOKEN=<oauth-access-token>
DEDALUS_MCP_SERVER_ONLY=1
```
Then run:
```bash
uv run python src/_client.py
```
`_client.py` builds a `SecretValues` credential with `base_url=ZENDESK_BASE_URL`, so the encrypted DAuth credential envelope points dispatch at the correct Zendesk account for that user.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues