#
tokens: 8276/50000 6/6 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── LICENSE
├── package.json
├── README.md
├── src
│   ├── index.ts
│   └── tools
│       └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | package-lock.json
 4 | 
 5 | # Build
 6 | dist/
 7 | build/
 8 | 
 9 | # TypeScript
10 | *.tsbuildinfo
11 | 
12 | # IDE
13 | .vscode/
14 | .idea/
15 | 
16 | # Environment
17 | .env
18 | .env.local
19 | .env.*.local
20 | 
21 | # Logs
22 | *.log
23 | npm-debug.log*
24 | 
25 | # OS
26 | .DS_Store
27 | Thumbs.db
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Zendesk MCP Server
  2 | 
  3 | [![npm version](https://badge.fury.io/js/zd-mcp-server.svg)](https://www.npmjs.com/package/zd-mcp-server)
  4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
  5 | 
  6 | A Model Context Protocol (MCP) server that provides AI assistants like Claude with seamless integration to Zendesk Support. Enables natural language interactions with Zendesk tickets, allowing you to search, create, update, and manage support tickets through conversational AI.
  7 | 
  8 | ## ✨ Features
  9 | 
 10 | - 🎫 **Complete Ticket Management**: Create, read, update, and search Zendesk tickets
 11 | - 💬 **Comments & Notes**: Add public comments and private internal notes
 12 | - 🔍 **Advanced Search**: Search tickets using Zendesk's powerful query syntax
 13 | - 🔗 **Incident Management**: Retrieve and manage linked incident tickets
 14 | - 🏷️ **Tag Management**: Add and manage ticket tags and metadata
 15 | - 🔒 **Secure Authentication**: Uses Zendesk API tokens for secure access
 16 | - 🚀 **Easy Installation**: Available via npm, npx, or manual setup
 17 | 
 18 | ## 🚀 Quick Start
 19 | 
 20 | ### Option 1: NPM Installation (Recommended)
 21 | 
 22 | ```bash
 23 | npm install -g zd-mcp-server
 24 | ```
 25 | 
 26 | ### Option 2: Use with npx (No Installation)
 27 | 
 28 | ```bash
 29 | npx zd-mcp-server
 30 | ```
 31 | 
 32 | ### Option 3: Development Setup
 33 | 
 34 | ```bash
 35 | git clone https://github.com/koundinya/zd-mcp-server.git
 36 | cd zd-mcp-server
 37 | npm install
 38 | npm run build
 39 | ```
 40 | 
 41 | ## ⚙️ Configuration
 42 | 
 43 | ### Environment Variables
 44 | 
 45 | Set these environment variables in your system or MCP client configuration:
 46 | 
 47 | ```bash
 48 | export ZENDESK_EMAIL="[email protected]"
 49 | export ZENDESK_TOKEN="your-zendesk-api-token"
 50 | export ZENDESK_SUBDOMAIN="your-company"  # from https://your-company.zendesk.com
 51 | ```
 52 | 
 53 | ### Claude Desktop Setup
 54 | 
 55 | Add to your Claude Desktop configuration file:
 56 | 
 57 | **Location:**
 58 | - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
 59 | - **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
 60 | 
 61 | **Configuration:**
 62 | 
 63 | ```json
 64 | {
 65 |   "mcpServers": {
 66 |     "zendesk": {
 67 |       "command": "npx",
 68 |       "args": ["-y", "zd-mcp-server"],
 69 |       "env": {
 70 |         "ZENDESK_EMAIL": "[email protected]",
 71 |         "ZENDESK_TOKEN": "your-zendesk-api-token",
 72 |         "ZENDESK_SUBDOMAIN": "your-company"
 73 |       }
 74 |     }
 75 |   }
 76 | }
 77 | ```
 78 | 
 79 | **Alternative (if installed globally):**
 80 | ```json
 81 | {
 82 |   "mcpServers": {
 83 |     "zendesk": {
 84 |       "command": "zd-mcp-server",
 85 |       "env": {
 86 |         "ZENDESK_EMAIL": "[email protected]",
 87 |         "ZENDESK_TOKEN": "your-zendesk-api-token",
 88 |         "ZENDESK_SUBDOMAIN": "your-company"
 89 |       }
 90 |     }
 91 |   }
 92 | }
 93 | ```
 94 | 
 95 | ### Cursor IDE Setup
 96 | 
 97 | Add to `~/.cursor/mcp.json` or `.cursor/mcp.json` in your project:
 98 | 
 99 | ```json
100 | {
101 |   "mcpServers": {
102 |     "zendesk": {
103 |       "command": "npx",
104 |       "args": ["-y", "zd-mcp-server"],
105 |       "env": {
106 |         "ZENDESK_EMAIL": "[email protected]",
107 |         "ZENDESK_TOKEN": "your-zendesk-api-token",
108 |         "ZENDESK_SUBDOMAIN": "your-company"
109 |       }
110 |     }
111 |   }
112 | }
113 | ```
114 | 
115 | ### Other MCP Clients
116 | 
117 | For other MCP-compatible clients (Cline, Windsurf, etc.), refer to their documentation for MCP server configuration. The server supports standard MCP protocols.
118 | 
119 | ## 🛠️ Available Tools
120 | 
121 | | Tool | Description | Example Usage |
122 | |------|-------------|---------------|
123 | | `zendesk_get_ticket` | Retrieve a ticket by ID | "Get ticket #12345" |
124 | | `zendesk_get_ticket_details` | Get detailed ticket with comments | "Show me full details for ticket #67890" |
125 | | `zendesk_search` | Search tickets with query syntax | "Find all urgent tickets from last week" |
126 | | `zendesk_create_ticket` | Create a new ticket | "Create a high priority ticket for login issues" |
127 | | `zendesk_update_ticket` | Update ticket properties | "Set ticket #555 to solved status" |
128 | | `zendesk_add_private_note` | Add internal agent notes | "Add a private note about investigation progress" |
129 | | `zendesk_add_public_note` | Add public customer comments | "Reply to customer with solution steps" |
130 | | `zendesk_get_linked_incidents` | Get incident tickets linked to problems | "Show incidents related to this problem ticket" |
131 | 
132 | ## 💬 Usage Examples
133 | 
134 | Once configured, you can use natural language with your AI assistant:
135 | 
136 | ### Ticket Management
137 | ```
138 | "Show me all high priority tickets assigned to me"
139 | "Create a new ticket: Customer can't access dashboard, priority urgent"
140 | "Update ticket #12345 status to pending and add a note about waiting for customer response"
141 | ```
142 | 
143 | ### Search & Discovery
144 | ```
145 | "Find all solved tickets from this week tagged with 'billing'"
146 | "Search for open tickets containing 'password reset'"
147 | "Show me tickets created by [email protected] in the last 30 days"
148 | ```
149 | 
150 | ### Customer Communication
151 | ```
152 | "Add a public comment to ticket #789: 'We've identified the issue and working on a fix'"
153 | "Add a private note: 'Customer confirmed the workaround is effective'"
154 | ```
155 | 
156 | ### Advanced Queries
157 | ```
158 | "Find all problem tickets that have linked incidents"
159 | "Show me escalated tickets that haven't been updated in 2 days"
160 | "Get details for ticket #456 including all comments and history"
161 | ```
162 | 
163 | ## 🔑 Authentication Setup
164 | 
165 | ### 1. Generate API Token
166 | 
167 | 1. Log in to your Zendesk account
168 | 2. Go to **Admin Center** → **Apps and integrations** → **APIs** → **Zendesk API**
169 | 3. Click **Add API token**
170 | 4. Add description: "MCP Server Integration"
171 | 5. Click **Create** and copy the token
172 | 6. **Important**: Save this token securely - you won't see it again
173 | 
174 | ### 2. Find Your Subdomain
175 | 
176 | Your Zendesk URL format: `https://YOUR-SUBDOMAIN.zendesk.com`
177 | Use `YOUR-SUBDOMAIN` as the `ZENDESK_SUBDOMAIN` value.
178 | 
179 | ### 3. Required Permissions
180 | 
181 | Ensure your Zendesk user account has:
182 | - **Agent** role (minimum)
183 | - **Ticket access** permissions
184 | - **API access** enabled
185 | 
186 | ## 🔧 Development
187 | 
188 | ### Project Structure
189 | ```
190 | zd-mcp-server/
191 | ├── src/
192 | │   ├── index.ts          # Server entry point
193 | │   └── tools/
194 | │       └── index.ts      # Zendesk tool implementations
195 | ├── dist/                 # Compiled JavaScript
196 | ├── package.json
197 | ├── tsconfig.json
198 | └── README.md
199 | ```
200 | 
201 | ### Building from Source
202 | ```bash
203 | git clone https://github.com/koundinya/zd-mcp-server.git
204 | cd zd-mcp-server
205 | npm install
206 | npm run build
207 | ```
208 | 
209 | ### Running Locally
210 | ```bash
211 | # Start the server
212 | npm start
213 | 
214 | # Development mode with auto-rebuild
215 | npm run dev
216 | ```
217 | 
218 | ### Testing
219 | ```bash
220 | # Test with MCP Inspector (if available)
221 | npx @modelcontextprotocol/inspector zd-mcp-server
222 | 
223 | # Or test the built version
224 | npx @modelcontextprotocol/inspector node dist/index.js
225 | ```
226 | 
227 | ## 🔍 Troubleshooting
228 | 
229 | ### Common Issues
230 | 
231 | **❌ "Authentication failed" errors**
232 | - Verify your API token is correct and hasn't expired
233 | - Ensure your email address matches your Zendesk account
234 | - Check that your subdomain is spelled correctly (no `.zendesk.com` suffix)
235 | 
236 | **❌ "Permission denied" errors**
237 | - Verify your Zendesk user has Agent permissions or higher
238 | - Ensure API access is enabled for your account
239 | - Check if your token has the required scopes
240 | 
241 | **❌ "Server not found" errors**
242 | - Ensure you've installed the package: `npm install -g zd-mcp-server`
243 | - Try using npx instead: `npx zd-mcp-server`
244 | - Check that your MCP client configuration file syntax is correct
245 | 
246 | **❌ "Environment variables not set" errors**
247 | - Verify all three environment variables are set: `ZENDESK_EMAIL`, `ZENDESK_TOKEN`, `ZENDESK_SUBDOMAIN`
248 | - Restart your MCP client after setting environment variables
249 | - Check for typos in environment variable names
250 | 
251 | ### Debug Mode
252 | 
253 | Enable debug logging:
254 | ```bash
255 | DEBUG=zd-mcp-server:* zd-mcp-server
256 | ```
257 | 
258 | ### Log Files
259 | 
260 | Check MCP client logs:
261 | - **Claude Desktop**: `~/Library/Logs/Claude/` (macOS) or `%APPDATA%/Claude/logs/` (Windows)
262 | - **Cursor**: Check the output panel for MCP server logs
263 | - **Terminal**: Run server directly to see real-time logs
264 | 
265 | ## 📚 Advanced Usage
266 | 
267 | ### Search Query Syntax
268 | 
269 | Zendesk search supports powerful query operators:
270 | 
271 | ```bash
272 | # Status-based searches
273 | status:open status:pending status:solved
274 | 
275 | # Priority searches  
276 | priority:urgent priority:high priority:normal priority:low
277 | 
278 | # Date-based searches
279 | created>2024-01-01 updated<2024-01-31
280 | 
281 | # Tag searches
282 | tags:billing tags:technical-issue
283 | 
284 | # Requester searches
285 | requester:[email protected]
286 | 
287 | # Complex combinations
288 | status:open priority:high created>2024-01-01 tags:billing
289 | ```
290 | 
291 | ### Batch Operations
292 | 
293 | While the server doesn't directly support batch operations, you can chain commands:
294 | 
295 | ```
296 | "Search for all urgent tickets, then show me details for the first 3 results"
297 | "Find tickets tagged 'billing', update them to normal priority, and add a note about the billing system maintenance"
298 | ```
299 | 
300 | ## 🤝 Contributing
301 | 
302 | Contributions are welcome! Please feel free to submit a Pull Request.
303 | 
304 | ### Development Setup
305 | 1. Fork the repository
306 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
307 | 3. Commit your changes (`git commit -m 'Add amazing feature'`)
308 | 4. Push to the branch (`git push origin feature/amazing-feature`)
309 | 5. Open a Pull Request
310 | 
311 | ### Reporting Issues
312 | 
313 | Found a bug? Please open an issue with:
314 | - Description of the problem
315 | - Steps to reproduce
316 | - Expected behavior
317 | - Your environment (OS, Node.js version, MCP client)
318 | - Relevant log outputs
319 | 
320 | ## 📄 License
321 | 
322 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
323 | 
324 | ## 🔗 Links
325 | 
326 | - **GitHub**: https://github.com/koundinya/zd-mcp-server
327 | - **npm**: https://www.npmjs.com/package/zd-mcp-server
328 | - **Zendesk API Docs**: https://developer.zendesk.com/api-reference/
329 | - **Model Context Protocol**: https://modelcontextprotocol.io/
330 | 
331 | ## 🆘 Support
332 | 
333 | - **Issues**: [GitHub Issues](https://github.com/koundinya/zd-mcp-server/issues)
334 | - **Zendesk API**: [Zendesk Developer Documentation](https://developer.zendesk.com/)
335 | - **MCP Protocol**: [MCP Documentation](https://modelcontextprotocol.io/docs/)
336 | 
337 | ---
338 | 
339 | Made with ❤️ for the MCP and Zendesk communities
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "ES2020",
 5 |     "moduleResolution": "node",
 6 |     "outDir": "./dist",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true
11 |   },
12 |   "include": ["src/**/*"]
13 | }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "zd-mcp-server",
 3 |   "version": "0.5.0",
 4 |   "description": "Zendesk MCP Server - Model Context Protocol server for Zendesk Support integration",
 5 |   "main": "dist/index.js",
 6 |   "bin": {
 7 |     "zd-mcp-server": "dist/index.js"
 8 |   },
 9 |   "type": "module",
10 |   "keywords": [
11 |     "mcp",
12 |     "model-context-protocol",
13 |     "zendesk",
14 |     "ai",
15 |     "claude",
16 |     "support",
17 |     "tickets"
18 |   ],
19 |   "author": "Girish Koundinya",
20 |   "license": "MIT",
21 |   "repository": {
22 |     "type": "git",
23 |     "url": "https://github.com/koundinya/zd-mcp-server.git"
24 |   },
25 |   "bugs": {
26 |     "url": "https://github.com/koundinya/zd-mcp-server/issues"
27 |   },
28 |   "homepage": "https://github.com/koundinya/zd-mcp-server#readme",
29 |   "dependencies": {
30 |     "@modelcontextprotocol/sdk": "^1.6.1",
31 |     "node-zendesk": "^2.1.0",
32 |     "zod": "^3.24.2"
33 |   },
34 |   "devDependencies": {
35 |     "@types/node": "^20.0.0",
36 |     "@types/node-zendesk": "^2.0.15",
37 |     "typescript": "^5.0.0",
38 |     "vitest": "^1.0.0"
39 |   },
40 |   "scripts": {
41 |     "build": "tsc",
42 |     "start": "node dist/index.js",
43 |     "dev": "tsc --watch",
44 |     "prepublishOnly": "npm run build"
45 |   },
46 |   "files": [
47 |     "dist/**/*",
48 |     "README.md",
49 |     "LICENSE"
50 |   ],
51 |   "engines": {
52 |     "node": ">=18.0.0"
53 |   }
54 | }
55 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 5 | import { zenDeskTools, createZendeskClient, searchTickets, getTicket, getTicketDetails, getLinkedIncidents } from "./tools/index.js";
 6 | 
 7 | // Re-export the functions for library usage
 8 | export { createZendeskClient, searchTickets, getTicket, getTicketDetails, getLinkedIncidents } from "./tools/index.js";
 9 | export type { ZendeskConfig } from "./tools/index.js";
10 | import { fileURLToPath } from "url";
11 | import { dirname, resolve } from "path";
12 | import { readFileSync } from "fs";
13 | 
14 | const __filename = fileURLToPath(import.meta.url);
15 | const __dirname = dirname(__filename);
16 | const packageJson = JSON.parse(
17 |   readFileSync(resolve(__dirname, "../package.json"), "utf8")
18 | );
19 | const VERSION = packageJson.version;
20 | 
21 | async function main() {
22 |   const server = new McpServer(
23 |     {
24 |       name: "zendesk-mcp",
25 |       version: VERSION,
26 |     },
27 |     {
28 |       capabilities: {
29 |         logging: {},
30 |       },
31 |     }
32 |   );
33 | 
34 |   zenDeskTools(server);
35 | 
36 |   const transport = new StdioServerTransport();
37 |   await server.connect(transport);
38 |   console.error(`Zendesk MCP Server v${VERSION} running on stdio`);
39 | }
40 | 
41 | main().catch((error) => {
42 |   console.error("Fatal error in main():", error);
43 |   process.exit(1);
44 | });
```

--------------------------------------------------------------------------------
/src/tools/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import type * as ZendeskTypes from "node-zendesk";
  2 | import zendesk from "node-zendesk";
  3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  4 | import { z } from "zod";
  5 | 
  6 | // Types for exported functions
  7 | export interface ZendeskConfig {
  8 |   email: string;
  9 |   token: string;
 10 |   subdomain: string;
 11 | }
 12 | 
 13 | // Create Zendesk client
 14 | export function createZendeskClient(config: ZendeskConfig) {
 15 |   return zendesk.createClient({
 16 |     username: config.email,
 17 |     token: config.token,
 18 |     remoteUri: `https://${config.subdomain}.zendesk.com/api/v2`,
 19 |   });
 20 | }
 21 | 
 22 | // Exported read-only tool functions
 23 | export async function getTicket(client: any, ticketId: number): Promise<any> {
 24 |   return new Promise((resolve, reject) => {
 25 |     client.tickets.show(ticketId, (error: Error | undefined, req: any, result: any) => {
 26 |       if (error) {
 27 |         reject(error);
 28 |       } else {
 29 |         resolve(result);
 30 |       }
 31 |     });
 32 |   });
 33 | }
 34 | 
 35 | export async function searchTickets(client: any, query: string): Promise<any> {
 36 |   return new Promise((resolve, reject) => {
 37 |     client.search.query(query, (error: Error | undefined, req: any, result: any) => {
 38 |       if (error) {
 39 |         reject(error);
 40 |       } else {
 41 |         resolve(result);
 42 |       }
 43 |     });
 44 |   });
 45 | }
 46 | 
 47 | export async function getTicketDetails(client: any, ticketId: number): Promise<any> {
 48 |   const ticketResult = await getTicket(client, ticketId);
 49 |   
 50 |   const commentsResult = await new Promise((resolve, reject) => {
 51 |     client.tickets.getComments(ticketId, (error: Error | undefined, req: any, result: any) => {
 52 |       if (error) {
 53 |         reject(error);
 54 |       } else {
 55 |         resolve(result);
 56 |       }
 57 |     });
 58 |   });
 59 | 
 60 |   return {
 61 |     ticket: ticketResult,
 62 |     comments: commentsResult
 63 |   };
 64 | }
 65 | 
 66 | export async function getLinkedIncidents(client: any, ticketId: number): Promise<any> {
 67 |   return new Promise((resolve, reject) => {
 68 |     client.tickets.listIncidents(ticketId, (error: Error | undefined, req: any, result: any) => {
 69 |       if (error) {
 70 |         reject(error);
 71 |       } else {
 72 |         resolve(result);
 73 |       }
 74 |     });
 75 |   });
 76 | }
 77 | 
 78 | // Environment-based client for backward compatibility
 79 | 
 80 | if (!process.env.ZENDESK_EMAIL || !process.env.ZENDESK_TOKEN || !process.env.ZENDESK_SUBDOMAIN) {
 81 |   throw new Error('Missing required environment variables: ZENDESK_EMAIL, ZENDESK_TOKEN, ZENDESK_SUBDOMAIN');
 82 | }
 83 | 
 84 | const client = zendesk.createClient({
 85 |   username: process.env.ZENDESK_EMAIL as string,
 86 |   token: process.env.ZENDESK_TOKEN as string,
 87 |   remoteUri: `https://${process.env.ZENDESK_SUBDOMAIN}.zendesk.com/api/v2`,
 88 | });
 89 | 
 90 | export function zenDeskTools(server: McpServer) {
 91 |   server.tool(
 92 |     "zendesk_get_ticket",
 93 |     "Get a Zendesk ticket by ID",
 94 |     {
 95 |       ticket_id: z.string().describe("The ID of the ticket to retrieve"),
 96 |     },
 97 |     async ({ ticket_id }) => {
 98 |       try {
 99 |         const result = await getTicket(client, parseInt(ticket_id, 10));
100 | 
101 |         return {
102 |           content: [{
103 |             type: "text",
104 |             text: JSON.stringify(result, null, 2)
105 |           }]
106 |         };
107 |       } catch (error: any) {
108 |         return {
109 |           content: [{
110 |             type: "text",
111 |             text: `Error: ${error.message || 'Unknown error occurred'}`
112 |           }],
113 |           isError: true
114 |         };
115 |       }
116 |     }
117 |   );
118 | 
119 |   server.tool(
120 |     "zendesk_update_ticket",
121 |     "Update a Zendesk ticket's properties",
122 |     {
123 |       ticket_id: z.string().describe("The ID of the ticket to update"),
124 |       subject: z.string().optional().describe("The new subject of the ticket"),
125 |       status: z.enum(['new', 'open', 'pending', 'hold', 'solved', 'closed']).optional().describe("The new status of the ticket"),
126 |       priority: z.enum(['low', 'normal', 'high', 'urgent']).optional().describe("The new priority of the ticket"),
127 |       type: z.enum(['problem', 'incident', 'question', 'task']).optional().describe("The new type of the ticket"),
128 |       assignee_id: z.string().optional().describe("The ID of the agent to assign the ticket to"),
129 |       tags: z.array(z.string()).optional().describe("Tags to set on the ticket (replaces existing tags)")
130 |     },
131 |     async ({ ticket_id, subject, status, priority, type, assignee_id, tags }) => {
132 |       try {
133 |         const ticketData: any = {
134 |           ticket: {}
135 |         };
136 | 
137 |         // Only add properties that are provided
138 |         if (subject) ticketData.ticket.subject = subject;
139 |         if (status) ticketData.ticket.status = status;
140 |         if (priority) ticketData.ticket.priority = priority;
141 |         if (type) ticketData.ticket.type = type;
142 |         if (assignee_id) ticketData.ticket.assignee_id = parseInt(assignee_id, 10);
143 |         if (tags) ticketData.ticket.tags = tags;
144 | 
145 |         const result = await new Promise((resolve, reject) => {
146 |           (client as any).tickets.update(parseInt(ticket_id, 10), ticketData, (error: Error | undefined, req: any, result: any) => {
147 |             if (error) {
148 |               console.log(error);
149 |               reject(error);
150 |             } else {
151 |               resolve(result);
152 |             }
153 |           });
154 |         });
155 | 
156 |         return {
157 |           content: [{
158 |             type: "text",
159 |             text: JSON.stringify(result, null, 2)
160 |           }]
161 |         };
162 |       } catch (error: any) {
163 |         return {
164 |           content: [{
165 |             type: "text",
166 |             text: `Error: ${error.message || 'Unknown error occurred'}`
167 |           }],
168 |           isError: true
169 |         };
170 |       }
171 |     }
172 |   );
173 | 
174 |   server.tool(
175 |     "zendesk_create_ticket",
176 |     "Create a new Zendesk ticket",
177 |     {
178 |       subject: z.string().describe("The subject of the ticket"),
179 |       description: z.string().describe("The initial description or comment for the ticket"),
180 |       priority: z.enum(['low', 'normal', 'high', 'urgent']).optional().describe("The priority of the ticket"),
181 |       status: z.enum(['new', 'open', 'pending', 'hold', 'solved', 'closed']).optional().describe("The status of the ticket"),
182 |       type: z.enum(['problem', 'incident', 'question', 'task']).optional().describe("The type of the ticket"),
183 |       tags: z.array(z.string()).optional().describe("Tags to add to the ticket")
184 |     },
185 |     async ({ subject, description, priority, status, type, tags }) => {
186 |       try {
187 |         const ticketData: any = {
188 |           ticket: {
189 |             subject,
190 |             comment: { body: description },
191 |           }
192 |         };
193 | 
194 |         if (priority) ticketData.ticket.priority = priority;
195 |         if (status) ticketData.ticket.status = status;
196 |         if (type) ticketData.ticket.type = type;
197 |         if (tags) ticketData.ticket.tags = tags;
198 | 
199 |         const result = await new Promise((resolve, reject) => {
200 |           (client as any).tickets.create(ticketData, (error: Error | undefined, req: any, result: any) => {
201 |             if (error) {
202 |               console.log(error);
203 |               reject(error);
204 |             } else {
205 |               resolve(result);
206 |             }
207 |           });
208 |         });
209 | 
210 |         return {
211 |           content: [{
212 |             type: "text",
213 |             text: JSON.stringify(result, null, 2)
214 |           }]
215 |         };
216 |       } catch (error: any) {
217 |         return {
218 |           content: [{
219 |             type: "text",
220 |             text: `Error: ${error.message || 'Unknown error occurred'}`
221 |           }],
222 |           isError: true
223 |         };
224 |       }
225 |     }
226 |   );
227 | 
228 |   server.tool(
229 |     "zendesk_add_private_note",
230 |     "Add a private internal note to a Zendesk ticket",
231 |     {
232 |       ticket_id: z.string().describe("The ID of the ticket to add a note to"),
233 |       note: z.string().describe("The content of the private note")
234 |     },
235 |     async ({ ticket_id, note }) => {
236 |       try {
237 |         const result = await new Promise((resolve, reject) => {
238 |           (client as any).tickets.update(parseInt(ticket_id, 10), {
239 |             ticket: {
240 |               comment: {
241 |                 body: note,
242 |                 public: false
243 |               }
244 |             }
245 |           }, (error: Error | undefined, req: any, result: any) => {
246 |             if (error) {
247 |               console.log(error);
248 |               reject(error);
249 |             } else {
250 |               resolve(result);
251 |             }
252 |           });
253 |         });
254 | 
255 |         return {
256 |           content: [{
257 |             type: "text",
258 |             text: JSON.stringify(result, null, 2)
259 |           }]
260 |         };
261 |       } catch (error: any) {
262 |         return {
263 |           content: [{
264 |             type: "text",
265 |             text: `Error: ${error.message || 'Unknown error occurred'}`
266 |           }],
267 |           isError: true
268 |         };
269 |       }
270 |     }
271 |   );
272 | 
273 |   server.tool(
274 |     "zendesk_add_public_note",
275 |     "Add a public comment to a Zendesk ticket",
276 |     {
277 |       ticket_id: z.string().describe("The ID of the ticket to add a comment to"),
278 |       comment: z.string().describe("The content of the public comment")
279 |     },
280 |     async ({ ticket_id, comment }) => {
281 |       try {
282 |         const result = await new Promise((resolve, reject) => {
283 |           (client as any).tickets.update(parseInt(ticket_id, 10), {
284 |             ticket: {
285 |               comment: {
286 |                 body: comment,
287 |                 public: true
288 |               }
289 |             }
290 |           }, (error: Error | undefined, req: any, result: any) => {
291 |             if (error) {
292 |               console.log(error);
293 |               reject(error);
294 |             } else {
295 |               resolve(result);
296 |             }
297 |           });
298 |         });
299 | 
300 |         return {
301 |           content: [{
302 |             type: "text",
303 |             text: JSON.stringify(result, null, 2)
304 |           }]
305 |         };
306 |       } catch (error: any) {
307 |         return {
308 |           content: [{
309 |             type: "text",
310 |             text: `Error: ${error.message || 'Unknown error occurred'}`
311 |           }],
312 |           isError: true
313 |         };
314 |       }
315 |     }
316 |   );
317 | 
318 |   server.tool(
319 |     "zendesk_search",
320 |     "Search for Zendesk tickets based on a query",
321 |     {
322 |       query: z.string().describe("Search query (e.g., 'status:open', 'priority:urgent', 'tags:need_help')"),
323 |     },
324 |     async ({ query }) => {
325 |       try {
326 |         const result = await searchTickets(client, query);
327 | 
328 |         return {
329 |           content: [{
330 |             type: "text",
331 |             text: JSON.stringify(result, null, 2)
332 |           }]
333 |         };
334 |       } catch (error: any) {
335 |         return {
336 |           content: [{
337 |             type: "text",
338 |             text: `Error: ${error.message || 'Unknown error occurred'}`
339 |           }],
340 |           isError: true
341 |         };
342 |       }
343 |     }
344 |   );
345 | 
346 |   server.tool(
347 |     "zendesk_get_ticket_details",
348 |     "Get detailed information about a Zendesk ticket including comments",
349 |     {
350 |       ticket_id: z.string().describe("The ID of the ticket to retrieve details for"),
351 |     },
352 |     async ({ ticket_id }) => {
353 |       try {
354 |         const result = await getTicketDetails(client, parseInt(ticket_id, 10));
355 | 
356 |         return {
357 |           content: [{
358 |             type: "text",
359 |             text: JSON.stringify(result, null, 2)
360 |           }]
361 |         };
362 |       } catch (error: any) {
363 |         return {
364 |           content: [{
365 |             type: "text",
366 |             text: `Error: ${error.message || 'Unknown error occurred'}`
367 |           }],
368 |           isError: true
369 |         };
370 |       }
371 |     }
372 |   );
373 | 
374 |   server.tool(
375 |     "zendesk_get_linked_incidents",
376 |     "Fetch all incident tickets linked to a particular ticket",
377 |     {
378 |       ticket_id: z.string().describe("The ID of the ticket to retrieve linked incidents for"),
379 |     },
380 |     async ({ ticket_id }) => {
381 |       try {
382 |         const result = await getLinkedIncidents(client, parseInt(ticket_id, 10));
383 | 
384 |         return {
385 |           content: [{
386 |             type: "text",
387 |             text: JSON.stringify(result, null, 2)
388 |           }]
389 |         };
390 |       } catch (error: any) {
391 |         return {
392 |           content: [{
393 |             type: "text",
394 |             text: `Error: ${error.message || 'Unknown error occurred'}`
395 |           }],
396 |           isError: true
397 |         };
398 |       }
399 |     }
400 |   );
401 | }
```