@fruggr/zendesk-mcp-server 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +250 -88
- package/dist/index.js +686 -97
- package/dist/index.js.map +1 -1
- package/glama.json +4 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
[](https://renovatebot.com)
|
|
8
8
|
[](https://github.com/semantic-release/semantic-release)
|
|
9
9
|
|
|
10
|
-
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that connects LLMs to the **Zendesk Support & Help Center APIs** — with per-user OAuth 2.1 PKCE authentication and fine-grained tool visibility controls.
|
|
10
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that connects LLMs to the **Zendesk Support & Help Center APIs** — with per-user OAuth 2.1 PKCE authentication and fine-grained tool visibility controls. Runs locally over **stdio** or as a private **remote MCP server** over HTTP.
|
|
11
11
|
|
|
12
12
|
## Why this server?
|
|
13
13
|
|
|
14
14
|
Most Zendesk integrations use a shared admin API key, giving every user full access to every ticket. This server takes a different approach:
|
|
15
15
|
|
|
16
|
-
- **Per-user authentication** —
|
|
16
|
+
- **Per-user authentication by default** — In both transports, the default is OAuth 2.1 PKCE: each user authenticates with their own Zendesk credentials, so the LLM sees exactly what the user is allowed to see. A static API-token escape hatch is documented below for stdio-only CI / headless contexts; it's refused at boot in HTTP mode.
|
|
17
|
+
- **Two deployment shapes, same auth story** — Run it on your laptop as a stdio MCP server (Claude Desktop / Claude Code / VS Code) or deploy it as a private remote MCP server with one user, one Zendesk session per HTTP request.
|
|
17
18
|
- **Context-friendly tool modes** — Expose 37 individual tools, 3 namespace proxies, or a single unified tool. Choose the mode that fits your LLM's context budget.
|
|
18
19
|
- **Section-based article editing** — For large Help Center articles, read and rewrite one section at a time (parsed by h1/h2/h3 headings) instead of shuffling the full HTML body through the LLM. Reduces tokens by 10–100× on targeted edits.
|
|
19
20
|
- **Read-only mode** — Restrict the server to read operations only, ideal for assistants that should never modify data.
|
|
20
|
-
- **
|
|
21
|
+
- **Lean stack** — Built on the official `@modelcontextprotocol/sdk` plus `zod`.
|
|
21
22
|
|
|
22
23
|
> Built and maintained by [Digital4better](https://digital4better.com) for the [Fruggr](https://www.fruggr.io) project.
|
|
23
24
|
|
|
@@ -28,13 +29,19 @@ Most Zendesk integrations use a shared admin API key, giving every user full acc
|
|
|
28
29
|
- You want an LLM to read or triage **Zendesk tickets** and **Help Center articles** on behalf of a real user, with that user's own permissions — not a shared admin key.
|
|
29
30
|
- You're editing **large Help Center articles** and want section-scoped reads/rewrites instead of round-tripping the full HTML body through the model.
|
|
30
31
|
- You need to **cap the tool surface** — read-only assistants, a single namespace, or one unified tool to fit a tight context budget.
|
|
31
|
-
- You run a **stdio MCP client** (Claude Desktop, Claude Code, Cursor, VS Code, Cline, …) and want a `npx`-installable server with no extra infrastructure.
|
|
32
|
+
- You run a **stdio MCP client** (Claude Desktop, Claude Code, Cursor, VS Code, Cline, …) and want a `npx`-installable server with no extra infrastructure, **or** you want to **deploy it as a private remote MCP server** that web/native clients reach over HTTP — each MCP client still carries its own user's OAuth token.
|
|
32
33
|
|
|
33
34
|
**Look elsewhere when:**
|
|
34
35
|
|
|
35
36
|
- You need Zendesk products outside Support & Guide (e.g. Talk, Explore analytics, Sell) — those endpoints aren't covered.
|
|
36
|
-
- You
|
|
37
|
-
|
|
37
|
+
- You need a single shared service account for all users — that's the opposite of this server's per-user OAuth model (the API-token escape hatch exists for stdio CI only, and is refused at boot in HTTP).
|
|
38
|
+
|
|
39
|
+
## Use cases
|
|
40
|
+
|
|
41
|
+
| Persona | Transport | Auth | Quick start |
|
|
42
|
+
|---------|-----------|------|-------------|
|
|
43
|
+
| **Run it on your laptop** — single user, plugged into Claude Desktop / Claude Code / VS Code | `stdio` (default) | OAuth 2.1 PKCE in your browser (or API token for CI) | [Quick start: local](#quick-start-local-stdio) |
|
|
44
|
+
| **Deploy a private remote MCP server** — one server per Zendesk account, each MCP client carries its own user's OAuth token | `http` | Per-user OAuth 2.1 PKCE bearer in `Authorization:` header; API token refused | [Quick start: remote](#quick-start-remote-http) |
|
|
38
45
|
|
|
39
46
|
## Tool modes
|
|
40
47
|
|
|
@@ -143,102 +150,197 @@ zendesk-mcp-server acme --namespace tickets
|
|
|
143
150
|
> Contributors and maintainers run the toolchain on a newer Node + pnpm —
|
|
144
151
|
> see [Development](#development).
|
|
145
152
|
|
|
146
|
-
##
|
|
153
|
+
## Quick start: local (stdio)
|
|
154
|
+
|
|
155
|
+
The default mode. One developer, one Zendesk account, OAuth 2.1 PKCE in the browser.
|
|
156
|
+
|
|
157
|
+
### Install
|
|
147
158
|
|
|
148
159
|
```bash
|
|
149
160
|
# Run without installing
|
|
150
161
|
npx -y @fruggr/zendesk-mcp-server <your-subdomain>
|
|
162
|
+
|
|
163
|
+
# Or install globally
|
|
164
|
+
npm install -g @fruggr/zendesk-mcp-server
|
|
165
|
+
zendesk-mcp-server <your-subdomain>
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
|
|
168
|
+
> Cloning from source and running a development branch is covered in the [Development](#development) section.
|
|
169
|
+
|
|
170
|
+
### Zendesk OAuth setup
|
|
171
|
+
|
|
172
|
+
1. Go to **Admin Center → Apps and integrations → APIs → OAuth Clients**
|
|
173
|
+
2. Create a **public** client:
|
|
174
|
+
- **Identifier**: `<your-subdomain>_zendesk` (or set `ZENDESK_OAUTH_CLIENT_ID`)
|
|
175
|
+
- **Redirect URL**: `http://localhost:27439/callback` (change the port to match
|
|
176
|
+
`ZENDESK_OAUTH_CALLBACK_PORT` / `--callback-port` if you override it; Zendesk
|
|
177
|
+
accepts several redirect URLs, one per line)
|
|
178
|
+
|
|
179
|
+
### Run
|
|
154
180
|
|
|
155
181
|
```bash
|
|
156
|
-
npm install -g @fruggr/zendesk-mcp-server
|
|
157
182
|
zendesk-mcp-server <your-subdomain>
|
|
158
183
|
```
|
|
159
184
|
|
|
160
|
-
|
|
185
|
+
On the first tool call, the server starts the sign-in flow: it opens a browser
|
|
186
|
+
window **and** returns a tool message containing the authorize URL. The call
|
|
187
|
+
does not block waiting for sign-in — authenticate in the browser (or open the
|
|
188
|
+
URL manually if it didn't open), then retry the request.
|
|
161
189
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
190
|
+
Once authenticated, the token is **persisted to disk** (one owner-only `0600`
|
|
191
|
+
file per subdomain in your OS config dir —
|
|
192
|
+
`%APPDATA%\fruggr\zendesk-mcp-server\<subdomain>.json` on Windows,
|
|
193
|
+
`${XDG_CONFIG_HOME:-~/.config}/fruggr/zendesk-mcp-server/<subdomain>.json`
|
|
194
|
+
elsewhere; override the path with `ZENDESK_TOKEN_FILE`). It is reused across restarts, so you don't
|
|
195
|
+
re-authenticate every time the MCP client respawns the server. If the Zendesk
|
|
196
|
+
OAuth client has token expiration enabled, the stored refresh token is used to
|
|
197
|
+
renew access silently; only an expired/invalid refresh token triggers a new
|
|
198
|
+
browser sign-in.
|
|
199
|
+
|
|
200
|
+
> **Port conflict?** If port `27439` is already in use the first tool call returns
|
|
201
|
+
> a clear error telling you to set `ZENDESK_OAUTH_CALLBACK_PORT` (or
|
|
202
|
+
> `--callback-port`) to a free port — remember to register the matching
|
|
203
|
+
> `http://localhost:<port>/callback` redirect URL in your Zendesk OAuth client.
|
|
204
|
+
|
|
205
|
+
> **API token escape hatch (stdio only).** For headless/CI environments where a browser is unavailable, set `ZENDESK_EMAIL` + `ZENDESK_API_TOKEN` (generate the token in **Admin Center → Apps and integrations → APIs → Zendesk API → Token Access**). The MCP server then uses Basic auth instead of starting the OAuth flow. This mode is **refused at boot in HTTP** because a shared static credential would expose every caller to the issuing user's rights.
|
|
206
|
+
|
|
207
|
+
### MCP client wiring
|
|
208
|
+
|
|
209
|
+
<details>
|
|
210
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
211
|
+
|
|
212
|
+
Add to your `claude_desktop_config.json`:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"mcpServers": {
|
|
217
|
+
"zendesk": {
|
|
218
|
+
"command": "npx",
|
|
219
|
+
"args": ["-y", "@fruggr/zendesk-mcp-server", "<your-subdomain>", "--mode", "single"]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
168
223
|
```
|
|
169
224
|
|
|
170
|
-
|
|
225
|
+
</details>
|
|
226
|
+
|
|
227
|
+
<details>
|
|
228
|
+
<summary><strong>Claude Code</strong></summary>
|
|
171
229
|
|
|
172
230
|
```bash
|
|
173
|
-
|
|
174
|
-
|
|
231
|
+
claude mcp add zendesk -- npx -y @fruggr/zendesk-mcp-server <your-subdomain> --mode single
|
|
232
|
+
```
|
|
175
233
|
|
|
176
|
-
|
|
177
|
-
|
|
234
|
+
</details>
|
|
235
|
+
|
|
236
|
+
<details>
|
|
237
|
+
<summary><strong>VS Code (Copilot / Continue / Cline)</strong></summary>
|
|
238
|
+
|
|
239
|
+
Add to your `.vscode/mcp.json`:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"servers": {
|
|
244
|
+
"zendesk": {
|
|
245
|
+
"command": "npx",
|
|
246
|
+
"args": ["-y", "@fruggr/zendesk-mcp-server", "<your-subdomain>", "--mode", "single"]
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
178
250
|
```
|
|
179
251
|
|
|
180
|
-
|
|
252
|
+
</details>
|
|
181
253
|
|
|
182
|
-
|
|
254
|
+
## Quick start: remote (HTTP)
|
|
183
255
|
|
|
184
|
-
|
|
256
|
+
> 🧪 **Experimental.** The HTTP transport is shipped but has not yet been
|
|
257
|
+
> exercised end-to-end against a real Zendesk tenant from every supported
|
|
258
|
+
> MCP client. Local stdio is the supported path. Until this notice is
|
|
259
|
+
> removed, expect rough edges around OAuth discovery behind reverse
|
|
260
|
+
> proxies, CORS with browser clients, and 401 / refresh flows — please
|
|
261
|
+
> open an issue with the symptoms you hit.
|
|
185
262
|
|
|
186
|
-
|
|
263
|
+
Deploy a private MCP server for **one** Zendesk account. Every MCP client connecting to the server presents its **own** user's OAuth bearer in `Authorization:` — the server never sees a shared admin key.
|
|
187
264
|
|
|
188
|
-
|
|
265
|
+
### Zendesk OAuth setup
|
|
189
266
|
|
|
190
|
-
|
|
191
|
-
2. Create a public client:
|
|
192
|
-
- **Identifier**: `<your-subdomain>_zendesk` (or set `ZENDESK_OAUTH_CLIENT_ID`)
|
|
193
|
-
- **Redirect URL**: `http://localhost:3000/callback`
|
|
267
|
+
Same procedure as the [local quick start](#zendesk-oauth-setup), with one difference: the **Redirect URL** must match the callback your MCP client uses — provided by the client itself, e.g. `https://claude.ai/oauth/callback` for claude.ai on the web. Check your client's docs.
|
|
194
268
|
|
|
195
|
-
|
|
269
|
+
### Run the server
|
|
196
270
|
|
|
197
271
|
```bash
|
|
198
|
-
zendesk-mcp-server <your-subdomain>
|
|
272
|
+
zendesk-mcp-server <your-subdomain> --transport http --port 3000 \
|
|
273
|
+
--public-url https://mcp.example.com
|
|
274
|
+
# stderr: Zendesk MCP server running via http on 0.0.0.0:3000
|
|
199
275
|
```
|
|
200
276
|
|
|
201
|
-
|
|
202
|
-
window **and** returns a tool message containing the authorize URL. The call
|
|
203
|
-
does not block waiting for sign-in — authenticate in the browser (or open the
|
|
204
|
-
URL manually if it didn't open), then retry the request. Once authenticated, the
|
|
205
|
-
token is cached in memory and subsequent calls succeed for the session.
|
|
277
|
+
### Public URL
|
|
206
278
|
|
|
207
|
-
|
|
279
|
+
`--public-url` (or `PUBLIC_URL=…`) is the URL **clients use to reach you**. It's what gets advertised in the OAuth discovery metadata as the canonical resource identifier (RFC 8707). When the server is behind a TLS reverse proxy — Azure App Service, Heroku, Fly.io, Cloudflare Tunnel, nginx, Caddy… — the bind host and the public URL differ, and spec-compliant MCP clients will refuse the connection if the metadata advertises the wrong resource. Without it the server boots in a degraded mode and prints a warning.
|
|
280
|
+
|
|
281
|
+
| Platform | Recommended setup |
|
|
282
|
+
|---|---|
|
|
283
|
+
| **Azure App Service** | Startup command: `PUBLIC_URL="https://$WEBSITE_HOSTNAME" zendesk-mcp-server $ZENDESK_SUBDOMAIN --transport http --port $PORT` |
|
|
284
|
+
| **Heroku / Fly / Cloud Run** | `PUBLIC_URL=https://<your-app>.<provider>.app` in the env / config |
|
|
285
|
+
| **Caddy / nginx / Traefik in front of a VM** | `PUBLIC_URL=https://mcp.example.com` |
|
|
286
|
+
| **Local dev (no proxy)** | `--host 127.0.0.1 --port 3000` — the resource URL is derived automatically (the wildcard `0.0.0.0` is what triggers the warning) |
|
|
208
287
|
|
|
209
|
-
|
|
288
|
+
### Authentication on every request
|
|
210
289
|
|
|
211
|
-
**Zendesk
|
|
290
|
+
`Authorization: Bearer …` is required on **every** `/mcp` request — a session id alone is never accepted as a credential. The most recent bearer presented on a session is the one used for Zendesk calls, so a client refreshing its token mid-session just works.
|
|
212
291
|
|
|
213
|
-
|
|
214
|
-
2. Enable **Token Access**, create a token
|
|
292
|
+
### Verify discovery endpoints
|
|
215
293
|
|
|
216
|
-
|
|
294
|
+
Served by the HTTP transport in `src/transports/http.ts`:
|
|
217
295
|
|
|
218
296
|
```bash
|
|
219
|
-
|
|
220
|
-
|
|
297
|
+
curl -s http://localhost:3000/.well-known/oauth-protected-resource
|
|
298
|
+
# → { "authorization_servers": ["https://<subdomain>.zendesk.com"], ... }
|
|
299
|
+
|
|
300
|
+
curl -s http://localhost:3000/.well-known/oauth-authorization-server
|
|
301
|
+
# → { "issuer": "https://<subdomain>.zendesk.com", "authorization_endpoint": "...", ... }
|
|
302
|
+
|
|
303
|
+
curl -s -i http://localhost:3000/healthz # → 200 OK
|
|
221
304
|
```
|
|
222
305
|
|
|
223
|
-
|
|
306
|
+
### MCP client wiring
|
|
307
|
+
|
|
308
|
+
Every major MCP client supports remote servers over Streamable HTTP and handles the OAuth 2.1 PKCE discovery flow natively — paste the URL, sign in once, you're connected. Replace `https://mcp.example.com` below with your deployed origin.
|
|
309
|
+
|
|
310
|
+
<details>
|
|
311
|
+
<summary><strong>Claude Code (CLI)</strong></summary>
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
claude mcp add zendesk --transport http https://mcp.example.com/mcp
|
|
315
|
+
```
|
|
224
316
|
|
|
225
|
-
|
|
317
|
+
</details>
|
|
226
318
|
|
|
227
319
|
<details>
|
|
228
320
|
<summary><strong>Claude Desktop</strong></summary>
|
|
229
321
|
|
|
230
|
-
Add
|
|
322
|
+
**Settings → Connectors → + Add custom connector**, paste `https://mcp.example.com/mcp`, click **Connect**. Claude Desktop drives the OAuth flow in your browser on first call.
|
|
323
|
+
|
|
324
|
+
</details>
|
|
325
|
+
|
|
326
|
+
<details>
|
|
327
|
+
<summary><strong>claude.ai (web)</strong></summary>
|
|
328
|
+
|
|
329
|
+
**Settings → Connectors → Add custom connector**, same URL. The OAuth flow runs in the same tab.
|
|
330
|
+
|
|
331
|
+
</details>
|
|
332
|
+
|
|
333
|
+
<details>
|
|
334
|
+
<summary><strong>VS Code (GitHub Copilot / Continue / Cline)</strong></summary>
|
|
335
|
+
|
|
336
|
+
Add to your `.vscode/mcp.json`:
|
|
231
337
|
|
|
232
338
|
```json
|
|
233
339
|
{
|
|
234
|
-
"
|
|
340
|
+
"servers": {
|
|
235
341
|
"zendesk": {
|
|
236
|
-
"
|
|
237
|
-
"
|
|
238
|
-
"env": {
|
|
239
|
-
"ZENDESK_EMAIL": "you@example.com",
|
|
240
|
-
"ZENDESK_API_TOKEN": "your-api-token"
|
|
241
|
-
}
|
|
342
|
+
"type": "http",
|
|
343
|
+
"url": "https://mcp.example.com/mcp"
|
|
242
344
|
}
|
|
243
345
|
}
|
|
244
346
|
}
|
|
@@ -247,31 +349,25 @@ Add to your `claude_desktop_config.json`:
|
|
|
247
349
|
</details>
|
|
248
350
|
|
|
249
351
|
<details>
|
|
250
|
-
<summary><strong>
|
|
352
|
+
<summary><strong>Cursor, Windsurf</strong></summary>
|
|
251
353
|
|
|
252
|
-
|
|
253
|
-
claude mcp add zendesk -- npx -y @fruggr/zendesk-mcp-server <your-subdomain> --mode single
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
For API token auth, set the env vars before launching Claude Code or add them to your shell profile.
|
|
354
|
+
Both expose an MCP settings UI that accepts a remote URL. Paste `https://mcp.example.com/mcp` and sign in when prompted.
|
|
257
355
|
|
|
258
356
|
</details>
|
|
259
357
|
|
|
260
358
|
<details>
|
|
261
|
-
<summary><strong>
|
|
359
|
+
<summary><strong>Zed</strong></summary>
|
|
262
360
|
|
|
263
|
-
|
|
361
|
+
Zed added native OAuth 2.0 + PKCE for Streamable HTTP MCP servers in 2026 ([zed-industries/zed#51768](https://github.com/zed-industries/zed/pull/51768)). Configure the remote server in your Zed settings; on first use Zed opens a loopback browser callback to complete the flow.
|
|
362
|
+
|
|
363
|
+
If you're on an older Zed build that predates that change, fall back to [`mcp-remote`](https://github.com/geelen/mcp-remote) as a local shim that does the OAuth flow on your machine and proxies the session:
|
|
264
364
|
|
|
265
365
|
```json
|
|
266
366
|
{
|
|
267
|
-
"
|
|
367
|
+
"context_servers": {
|
|
268
368
|
"zendesk": {
|
|
269
369
|
"command": "npx",
|
|
270
|
-
"args": ["-y", "
|
|
271
|
-
"env": {
|
|
272
|
-
"ZENDESK_EMAIL": "you@example.com",
|
|
273
|
-
"ZENDESK_API_TOKEN": "your-api-token"
|
|
274
|
-
}
|
|
370
|
+
"args": ["-y", "mcp-remote", "https://mcp.example.com/mcp"]
|
|
275
371
|
}
|
|
276
372
|
}
|
|
277
373
|
}
|
|
@@ -279,7 +375,33 @@ Add to your `.vscode/mcp.json`:
|
|
|
279
375
|
|
|
280
376
|
</details>
|
|
281
377
|
|
|
282
|
-
|
|
378
|
+
On the first call the MCP client fetches the discovery metadata, performs the OAuth 2.1 PKCE flow against Zendesk on behalf of the **end user**, and sends the resulting access token as a `Bearer` to the server. Each subsequent tool call runs with that user's Zendesk permissions.
|
|
379
|
+
|
|
380
|
+
### CORS
|
|
381
|
+
|
|
382
|
+
The HTTP transport ships a default CORS allowlist that covers today's major **browser-based** MCP clients out of the box (ordered by user base): `chatgpt.com`, `claude.ai`, `gemini.google.com`, `copilot.microsoft.com`, `perplexity.ai`, `chat.mistral.ai`, `grok.com`, plus `chat.openai.com`. Localhost on any port (MCP Inspector, dev pages) is also always allowed.
|
|
383
|
+
|
|
384
|
+
**Native MCP clients** (Claude Desktop / Claude Code CLI / Cursor / VS Code / Zed) send no `Origin` header — CORS doesn't apply to them, they work regardless.
|
|
385
|
+
|
|
386
|
+
To allow an additional browser origin (custom dashboard, internal portal), pass `--cors-origin` (repeatable) or set `CORS_ORIGIN` as a comma-separated list:
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
zendesk-mcp-server acme --transport http --port 3000 \
|
|
390
|
+
--cors-origin https://internal-dashboard.example.com \
|
|
391
|
+
--cors-origin https://team-portal.example.com
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
The defaults are always applied — your additions extend them, they don't replace them.
|
|
395
|
+
|
|
396
|
+
### Operator responsibilities
|
|
397
|
+
|
|
398
|
+
This server provides the MCP transport and the OAuth discovery metadata. The operator is still responsible for:
|
|
399
|
+
|
|
400
|
+
- **TLS termination** (put the server behind a reverse proxy like Caddy / nginx / Cloudflare Tunnel)
|
|
401
|
+
- **Network exposure & firewall** (the server binds `0.0.0.0` by default — choose carefully)
|
|
402
|
+
- **Process supervision** (systemd, Docker, fly.io, your hosting provider's runner — none is shipped here)
|
|
403
|
+
|
|
404
|
+
## CLI reference
|
|
283
405
|
|
|
284
406
|
```
|
|
285
407
|
zendesk-mcp-server <subdomain> [options]
|
|
@@ -290,6 +412,15 @@ Options:
|
|
|
290
412
|
--tool <name> Filter by tool name (repeatable, forces --mode all)
|
|
291
413
|
--read-only Only expose read operations
|
|
292
414
|
--log-level <level> debug | info (default) | warn | error
|
|
415
|
+
--transport <t> stdio (default) | http
|
|
416
|
+
--host <host> HTTP bind host (default: 0.0.0.0)
|
|
417
|
+
--port <port> HTTP bind port (default: 3000; 0 = OS-assigned)
|
|
418
|
+
--public-url <url> Public URL clients use to reach the server (HTTP mode,
|
|
419
|
+
required behind a TLS reverse proxy)
|
|
420
|
+
--cors-origin <url> Extra browser origin allowed by CORS (repeatable;
|
|
421
|
+
adds to the default allowlist of major web MCP
|
|
422
|
+
clients + localhost-any-port)
|
|
423
|
+
--callback-port <port> Local OAuth callback port for stdio (default 27439)
|
|
293
424
|
```
|
|
294
425
|
|
|
295
426
|
`--namespace` and `--read-only` are applied before the proxies are registered, so they narrow the surface in every mode — in the default `namespace` mode, `--namespace help_center` registers a single proxy (`zendesk_help_center`) instead of three.
|
|
@@ -297,7 +428,7 @@ Options:
|
|
|
297
428
|
**Examples:**
|
|
298
429
|
|
|
299
430
|
```bash
|
|
300
|
-
#
|
|
431
|
+
# Local single-tool mode — minimal context, all 37 operations in one tool
|
|
301
432
|
zendesk-mcp-server acme --mode single
|
|
302
433
|
|
|
303
434
|
# Read-only tickets only
|
|
@@ -305,19 +436,30 @@ zendesk-mcp-server acme --read-only --namespace tickets
|
|
|
305
436
|
|
|
306
437
|
# Cherry-pick specific tools
|
|
307
438
|
zendesk-mcp-server acme --tool get_ticket --tool search_tickets --tool get_current_user
|
|
439
|
+
|
|
440
|
+
# Remote HTTP, read-only Help Center surface
|
|
441
|
+
zendesk-mcp-server acme --transport http --port 8080 \
|
|
442
|
+
--namespace help_center --read-only
|
|
308
443
|
```
|
|
309
444
|
|
|
310
|
-
|
|
445
|
+
## Environment variables
|
|
311
446
|
|
|
312
447
|
| Variable | Required | Default | Description |
|
|
313
448
|
|----------|----------|---------|-------------|
|
|
314
449
|
| `ZENDESK_SUBDOMAIN` | yes (or CLI arg) | — | Zendesk subdomain (e.g., `acme` for acme.zendesk.com) |
|
|
315
450
|
| `ZENDESK_OAUTH_CLIENT_ID` | no | `<subdomain>_zendesk` | OAuth client identifier |
|
|
316
|
-
| `
|
|
317
|
-
| `
|
|
451
|
+
| `ZENDESK_OAUTH_CALLBACK_PORT` | no | `27439` | Local port for the OAuth browser callback (also `--callback-port`). Must match the redirect URL registered in Zendesk. **stdio only**. |
|
|
452
|
+
| `ZENDESK_TOKEN_FILE` | no | OS config dir | Path to the persisted OAuth token file (`0600`). |
|
|
453
|
+
| `ZENDESK_EMAIL` | stdio API-token only | — | Agent email for Basic auth — **refused in HTTP** |
|
|
454
|
+
| `ZENDESK_API_TOKEN` | stdio API-token only | — | Zendesk API token — **refused in HTTP** |
|
|
455
|
+
| `TRANSPORT` | no | `stdio` | `stdio` or `http` |
|
|
456
|
+
| `HOST` | no | `0.0.0.0` | HTTP bind host |
|
|
457
|
+
| `PORT` | no | `3000` | HTTP bind port (`0` to let the OS pick) |
|
|
458
|
+
| `PUBLIC_URL` | recommended in HTTP behind a proxy | derived from host:port | Public URL advertised in OAuth discovery metadata |
|
|
459
|
+
| `CORS_ORIGIN` | no | — | Comma-separated browser origins added to the default CORS allowlist |
|
|
318
460
|
| `LOG_LEVEL` | no | `info` | Log verbosity (`debug` surfaces the full OAuth flow trace) |
|
|
319
461
|
|
|
320
|
-
|
|
462
|
+
In stdio, if both `ZENDESK_EMAIL` and `ZENDESK_API_TOKEN` are set, the server uses API token auth; otherwise it uses OAuth 2.1 PKCE. In HTTP mode, API token credentials are refused at boot — only per-user OAuth 2.1 PKCE is accepted. Full API-token setup is documented in [`docs/api-token-stdio.md`](docs/api-token-stdio.md).
|
|
321
463
|
|
|
322
464
|
## Troubleshooting
|
|
323
465
|
|
|
@@ -340,6 +482,22 @@ When the browser fails to open, look for the `oauth_browser_open_failed` event:
|
|
|
340
482
|
it reports the underlying error, the platform, and which environment markers are
|
|
341
483
|
present (no secrets, tokens, or env values are ever logged).
|
|
342
484
|
|
|
485
|
+
### The OAuth callback port is already in use
|
|
486
|
+
|
|
487
|
+
The sign-in flow runs a short-lived local server on port `27439` to receive the
|
|
488
|
+
callback. If that port is taken, the first tool call fails with a message saying
|
|
489
|
+
so (and logs `oauth_callback_listen_failed`). Pick a free port with
|
|
490
|
+
`ZENDESK_OAUTH_CALLBACK_PORT=<port>` (or `--callback-port <port>`), and register
|
|
491
|
+
the matching `http://localhost:<port>/callback` redirect URL in your Zendesk
|
|
492
|
+
OAuth client.
|
|
493
|
+
|
|
494
|
+
### I have to re-authenticate every time
|
|
495
|
+
|
|
496
|
+
The OAuth token is persisted to an owner-only file in your OS config dir and
|
|
497
|
+
reused across restarts, so this shouldn't happen. If it does, check that the file
|
|
498
|
+
is writable (`ZENDESK_TOKEN_FILE` to relocate it) and look for
|
|
499
|
+
`token_persist_failed` in the logs.
|
|
500
|
+
|
|
343
501
|
Where each client writes the server's stderr:
|
|
344
502
|
|
|
345
503
|
| Client | Log location |
|
|
@@ -364,26 +522,30 @@ test the project. The **published package** still runs on Node 20+ (see
|
|
|
364
522
|
and runs the smoke test to keep that promise honest.
|
|
365
523
|
|
|
366
524
|
```bash
|
|
367
|
-
#
|
|
368
|
-
|
|
525
|
+
# Clone, install, build
|
|
526
|
+
git clone https://github.com/fruggr/zendesk-mcp-server.git
|
|
527
|
+
cd zendesk-mcp-server && pnpm install && pnpm build
|
|
528
|
+
node dist/index.js <your-subdomain>
|
|
369
529
|
|
|
370
|
-
# Dev mode (
|
|
371
|
-
|
|
372
|
-
pnpm dev -- <your-subdomain> --mode all
|
|
530
|
+
# Dev mode, OAuth (browser opens on first tool call)
|
|
531
|
+
pnpm dev -- <your-subdomain> --mode all
|
|
373
532
|
|
|
374
|
-
#
|
|
375
|
-
pnpm
|
|
533
|
+
# Dev mode, HTTP transport (OAuth bearer from the MCP client)
|
|
534
|
+
pnpm dev -- <your-subdomain> --transport http --port 3000 --public-url http://localhost:3000
|
|
376
535
|
|
|
377
|
-
#
|
|
378
|
-
pnpm typecheck
|
|
536
|
+
# Build / typecheck / lint / test
|
|
537
|
+
pnpm build && pnpm typecheck && pnpm check && pnpm test
|
|
538
|
+
```
|
|
379
539
|
|
|
380
|
-
|
|
381
|
-
pnpm check
|
|
540
|
+
To test a PR branch without publishing to npm — the `prepare` script builds on install:
|
|
382
541
|
|
|
383
|
-
|
|
384
|
-
|
|
542
|
+
```bash
|
|
543
|
+
npx -y github:fruggr/zendesk-mcp-server <your-subdomain>
|
|
544
|
+
npx -y github:fruggr/zendesk-mcp-server#my-feature-branch <your-subdomain>
|
|
385
545
|
```
|
|
386
546
|
|
|
547
|
+
Contributor conventions (architecture, code style, submission bar, release workflow) live in [`AGENTS.md`](AGENTS.md).
|
|
548
|
+
|
|
387
549
|
## Inspiration & related projects
|
|
388
550
|
|
|
389
551
|
This project was built with reference to:
|