# 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 | [](https://www.npmjs.com/package/zd-mcp-server) 4 | [](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 | } ```