Skip to main content
Glama
imohanvadivel

Zoho Desk MCP Server

README.md
# desk_mcp

An MCP (Model Context Protocol) server that exposes the entire Zoho Desk API (706 endpoints across 130 categories) through 3 context-efficient tools.

Instead of registering one tool per endpoint (which would flood the LLM context), the server uses a **search-then-call** pattern:

1. **`list_categories`** — discover API areas
2. **`search_endpoints`** — find the right endpoint by keyword/category
3. **`call_api`** — execute it by `operationId`

## Prerequisites

- [Bun](https://bun.sh) v1.3+
- A Zoho Desk account with API access
- A valid Zoho OAuth token

## Installation

```bash
bun install
```

## Configuration

The server reads three environment variables:

| Variable | Required | Default | Description |
|---|---|---|---|
| `ZOHO_OAUTH_TOKEN` | Yes | — | Your Zoho OAuth token (see [Authorization](#authorization) below) |
| `ZOHO_ORG_ID` | Yes | — | Your Zoho Desk organization ID. Auto-injected into every API call. |
| `ZOHO_DESK_DOMAIN` | No | `desk.zoho.com` | Zoho Desk API domain. Use `desk.zoho.eu`, `desk.zoho.in`, `desk.zoho.com.au`, `desk.zoho.jp`, or `desk.zoho.com.cn` for other data centers. |

## Authorization

Zoho Desk APIs use OAuth 2.0. To get your token:

1. **Register a Zoho API client** at [Zoho API Console](https://api-console.zoho.com/)
   - Choose "Self Client" for quick testing, or "Server-based Application" for production
2. **Generate a grant token** with the scopes your operations need (e.g., `Desk.tickets.READ`, `Desk.tickets.CREATE`). Common scopes:
   - `Desk.tickets.READ` / `CREATE` / `UPDATE` / `DELETE`
   - `Desk.contacts.READ` / `CREATE` / `UPDATE` / `DELETE`
   - `Desk.settings.READ` / `CREATE` / `UPDATE` / `DELETE`
   - `Desk.basic.READ` / `CREATE` / `UPDATE` / `DELETE`
   - `Desk.search.READ`
   - `Desk.articles.READ` / `CREATE` / `UPDATE` / `DELETE`
   - `Desk.tasks.READ` / `CREATE` / `UPDATE` / `DELETE`
3. **Exchange the grant token for an access token** via:
   ```
   POST https://accounts.zoho.com/oauth/v2/token
     ?grant_type=authorization_code
     &client_id=YOUR_CLIENT_ID
     &client_secret=YOUR_CLIENT_SECRET
     &code=YOUR_GRANT_TOKEN
   ```
4. Use the returned `access_token` as `ZOHO_OAUTH_TOKEN`. For long-lived access, store the `refresh_token` and exchange it before expiry.

**Finding your Org ID:** Call `GET https://desk.zoho.com/api/v1/organizations` with your token. The `id` field in the response is your `ZOHO_ORG_ID`.

## Running the Server

```bash
bun run index.ts
```

The server communicates over **stdio** using the MCP protocol.

## MCP Integration

### Claude Code

Add to your Claude Code MCP settings (`~/.claude/settings.json` or project-level `.claude/settings.json`):

```json
{
  "mcpServers": {
    "zoho-desk": {
      "command": "bun",
      "args": ["/absolute/path/to/desk_mcp/index.ts"],
      "env": {
        "ZOHO_OAUTH_TOKEN": "your-oauth-token",
        "ZOHO_ORG_ID": "your-org-id",
        "ZOHO_DESK_DOMAIN": "desk.zoho.com"
      }
    }
  }
}
```

### Claude Desktop

Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "zoho-desk": {
      "command": "bun",
      "args": ["/absolute/path/to/desk_mcp/index.ts"],
      "env": {
        "ZOHO_OAUTH_TOKEN": "your-oauth-token",
        "ZOHO_ORG_ID": "your-org-id",
        "ZOHO_DESK_DOMAIN": "desk.zoho.com"
      }
    }
  }
}
```

### Any MCP Client

The server uses the standard `StdioServerTransport`. Any MCP-compatible client can connect by spawning the process and communicating over stdin/stdout.

## Tools

### `list_categories`

Lists all 130 Zoho Desk API categories with their endpoint counts.

**Parameters:** None

**Example response:**
```
130 categories, 706 total endpoints:

Account (14 endpoints)
Agent (18 endpoints)
Article (9 endpoints)
...
Ticket (27 endpoints)
```

### `search_endpoints`

Searches endpoints by keyword and/or category. Returns matching operations with full details: `operationId`, HTTP method, path, summary, description, parameters, and whether the endpoint accepts a request body.

**Parameters:**

| Name | Type | Required | Description |
|---|---|---|---|
| `query` | string | No | Search keywords (e.g., `"create ticket"`, `"list agents"`). Matches against operationId, summary, description, path, and tags. Multiple words use AND logic. |
| `category` | string | No | Filter by category (e.g., `"Ticket"`, `"Agent"`). Case-insensitive. |
| `limit` | number | No | Max results to return. Default: 20. |

**Example response:**
```json
[
  {
    "operationId": "getTicket",
    "method": "GET",
    "path": "/api/v1/tickets/{ticketId}",
    "category": "Ticket",
    "summary": "Get a ticket",
    "description": "This API fetches a ticket by its ID.",
    "parameters": [
      { "name": "ticketId", "in": "path", "required": true, "description": "ID of the ticket" },
      { "name": "include", "in": "query", "required": false, "description": "..." }
    ],
    "hasBody": false
  }
]
```

### `call_api`

Executes a Zoho Desk API endpoint by its `operationId`.

**Parameters:**

| Name | Type | Required | Description |
|---|---|---|---|
| `operationId` | string | Yes | The operationId from `search_endpoints` results. |
| `params` | object | No | Parameter values. Keys are parameter names. Path, query, and header params go by name. For endpoints with a request body, include a `body` key with the JSON payload. |

**Example — Get a ticket:**
```json
{
  "operationId": "getTicket",
  "params": {
    "ticketId": "12345000000123"
  }
}
```

**Example — Create a ticket:**
```json
{
  "operationId": "createTicket",
  "params": {
    "body": {
      "subject": "Printer not working",
      "departmentId": "12345000000006907",
      "contactId": "12345000000042032",
      "description": "The office printer on floor 3 is jammed."
    }
  }
}
```

**Example — Search tickets:**
```json
{
  "operationId": "searchTickets",
  "params": {
    "searchStr": "printer",
    "limit": "5"
  }
}
```

## Typical Workflow

```
User: "How many open tickets are assigned to me?"

LLM calls: search_endpoints(query="list tickets assigned")
LLM calls: call_api(operationId="getAssociatedTickets", params={ "status": "open" })
LLM: "You have 12 open tickets assigned to you. Here are the most recent..."
```

## API Categories

The server indexes 706 endpoints across these 130 categories:

<details>
<summary>View all categories</summary>

Account, AccountAttachment, AccountComments, AccountContactMappingInfo, AccountDeduplication, AccountFollowers, AccountSla, AccountTimeEntry, Activity, Agent, AgentPresence, AgentSignatures, AgentTimeEntry, Article, ArticleAttachment, ArticleComment, ArticleFeedback, ArticleTranslation, AutomationEngine, AutomationFeatureCount, Backup, Badge, BugInteg, BulkImport, BusinessHour, Call, CallComments, Channel, Community, CommunityAttachment, CommunityCategory, CommunityComment, CommunityPreferences, CommunityTopic, CommunityUser, Contact, ContactAttachment, ContactComments, ContactDeduplication, ContactFollowers, ContactProfile, ContactTimeEntry, Contract, CountriesAndLanguages, CustomView, CustomerHappiness, DashboardMetrics, Dashboards, Department, DependencyMappings, DisplayEntity, DomainMapping, EmailFailureAlert, EmailTemplates, EntityBlueprints, Event, EventComments, Field, Finance, Followers, GenericAction, Helpcenter, HelpcenterGroups, HolidayList, IMCannedMessage, IMTemplateMessage, IM_Channel, IM_Message, IM_Metrics, IM_Session, Import, KBRootCategory, KBSection, KbCategory, KbCategoryLogo, Label, Layout, LayoutRuleCriteria, LayoutRules, LicenseFeaturePlan, MailReplyAddress, Module, NewTicketHistory, Organization, PendingApproval, Permalink, PinnedConversation, Product, ProductAttachment, Profile, Recyclebin, ReportIntegration, Role, RoutingPreference, RuleGroup, Search, SharingRule, Skill, SkillConfiguration, SkillType, SubjectAccessRequest, SupportEmailDomain, SupportPlan, Task, TaskAttachment, TaskComments, TaskTimeEntry, TaskTimer, Team, TemplateFolders, Thread, Ticket, TicketApprovals, TicketAttachment, TicketComment, TicketCount, TicketFollowers, TicketTag, TicketTemplate, TicketTimeEntry, TicketTimer, TimeTracking, Upload, User, ValidationRuleCriteria, ValidationRules, Webhook, Widget, blueprintTransitions, blueprints

</details>

## Testing

```bash
bun test
```

Runs 70 tests covering:
- `$ref` resolution (local and cross-file)
- OAS type parsing
- Parameter resolution and request body detection
- Operation collection and deduplication
- URL building with path/query/header params
- Tool description generation
- Search and category filtering
- Integration tests against all 155 OAS files (validates all 706 endpoints)

## Project Structure

```
desk_mcp/
  index.ts          # MCP server — loads OAS specs, registers 3 tools
  index.test.ts     # Test suite (70 tests)
  OAS/              # OpenAPI 3.1 spec files (155 JSON files)
    Ticket.json
    Agent.json
    Contact.json
    Common.json     # Shared components ($ref target)
    ...
  package.json
  tsconfig.json
```

Maintenance

ActivityInactive
ResponsivenessNo issues