Freshdesk MCP Server: Complete Setup Guide for AI Agents
If you want an AI agent — Claude, a Cursor-based coding assistant, or a custom LLM workflow — to autonomously read, create, and update Freshdesk support tickets, you need a Freshdesk MCP Server. This guide covers everything from what that actually means to a working production setup, including architecture, authentication, available tools, real-world workflows, and the sharp edges you'll hit in production.
Quick answer: Freshworks does not currently ship an official general-purpose MCP Server. Your options are open-source community implementations (Node.js/Python wrappers around the Freshdesk REST API v2) and managed integration platforms (Composio, Pipedream, Zapier). This guide explains all options and walks through a full setup using Composio, the most documented and actively maintained managed approach as of early 2025.
What Is a Freshdesk MCP Server?
The Model Context Protocol (MCP) is an open standard that lets AI models discover and call external tools through a structured JSON-RPC 2.0 interface. An MCP server exposes a set of tools — callable functions with typed inputs and outputs — that an AI agent can invoke to interact with the outside world.
A Freshdesk MCP Server is an MCP-compliant server that wraps the Freshdesk REST API v2 and exposes Freshdesk operations (create ticket, list tickets, update status, add note, etc.) as MCP tools. When an AI agent like Claude Desktop connects to this server, it can handle customer support tasks — triaging tickets, summarizing conversations, routing requests, drafting replies — without a human writing explicit API calls.
The key insight: MCP turns Freshdesk API endpoints into AI-callable tools. The LLM decides at runtime which tool to call based on context, not pre-written logic.
Does Freshworks Offer an Official MCP Server?
As of January 2025, Freshworks has not released an official, general-purpose MCP Server for Freshdesk or any other product in the Freshworks suite. There is no official @freshworks/mcp-server-freshdesk package, no Freshworks-hosted MCP endpoint, and no official Freshdesk MCP documentation.
What exists instead:
- Community-built self-hosted MCP servers — open-source projects that wrap the Freshdesk REST API using the MCP SDK
- Managed integration platforms — Composio, Pipedream, and Zapier, which expose Freshdesk actions through MCP-compatible interfaces as part of their broader integration offering
This matters because tool availability, reliability guarantees, maintenance cadence, and security posture differ significantly between these approaches.
Freshdesk MCP Ecosystem Overview
Community Self-Hosted Implementations
Several developers have published open-source MCP servers for Freshdesk on GitHub. These are typically:
- Written in TypeScript/Node.js using the
@modelcontextprotocol/sdkpackage, or in Python using themcpPython SDK - Deployed locally or on a private server, communicating via stdio transport with the MCP client
- Authenticated using a Freshdesk API key passed as an environment variable
- Exposing a subset of Freshdesk REST API v2 operations as MCP tools
Trade-offs: Full control over code and data. No third-party services in the data path. Requires maintenance — SDK updates, Freshdesk API changes, and security patches are your responsibility.
Composio
Composio is a managed integration platform with MCP support. It provides pre-built Freshdesk tool sets, handles authentication (API key and OAuth flows), and exposes tools through an MCP-compatible interface. Tools are documented and regularly updated by the Composio team.
Trade-offs: Fast setup, maintained by a vendor, but customer data transits Composio's infrastructure. Requires trusting a third-party platform with your Freshdesk credentials.
Pipedream
Pipedream offers an MCP Server feature that can expose Freshdesk actions (from its pre-built Freshdesk app) as MCP tools. It's workflow-based: you configure a Pipedream workflow that includes Freshdesk steps, then expose it via an MCP endpoint.
Trade-offs: Highly flexible for multi-step workflows. Requires Pipedream account. Good for teams already using Pipedream for automation.
Zapier MCP
Zapier has introduced an MCP interface for its action library. Freshdesk actions available in Zapier (create ticket, update ticket, etc.) can theoretically be exposed through Zapier's MCP endpoint. Setup is managed through the Zapier dashboard.
Trade-offs: Very easy to configure for non-technical users. Less flexibility for complex tool schemas or custom tool logic. Zapier's Freshdesk action set may be less complete than the direct API.
n8n
n8n is an open-source workflow automation tool with a Freshdesk node. While n8n has an AI agent framework and MCP-related features, native MCP server exposure of Freshdesk nodes is not as straightforwardly documented as Composio or Pipedream as of early 2025. n8n is best suited for teams building automated workflows with AI agent steps, rather than serving MCP endpoints to external clients.
Want to analyze your API security?
Import your OpenAPI spec and generate a Security Report automatically.
Comparison Table: Freshdesk MCP Approaches
| Attribute | Community OSS Server | Composio | Pipedream | Zapier MCP |
|---|---|---|---|---|
| Official/Vendor-backed | Community | Vendor (Composio) | Vendor (Pipedream) | Vendor (Zapier) |
| Self-hosted vs Managed | Self-hosted | Managed (cloud) | Managed (cloud) | Managed (cloud) |
| Setup method | Clone repo, set env vars, run server | Account signup, connect Freshdesk, get MCP URL | Pipedream account, configure workflow, expose MCP endpoint | Zapier account, enable MCP, connect Freshdesk |
| Authentication | Freshdesk API key (env var) | API key or OAuth via Composio vault | API key via Pipedream credentials | API key via Zapier account |
| Supported operations | Depends on implementation; typically core ticket CRUD | Documented Freshdesk tool set (create, list, update, get, add note) | Pipedream Freshdesk app actions | Zapier Freshdesk action library |
| Supported MCP clients | Any stdio-compatible client (Claude Desktop, Cursor) | Claude Desktop, Cursor, Claude Code, any MCP client | Any MCP client | Claude, others |
| Customization | Full — modify source code | Limited to available tools; some config options | High — build custom workflow steps | Low |
| Data residency | Your infrastructure | Composio cloud | Pipedream cloud | Zapier cloud |
| Production suitability | High (if maintained) | Medium-High | Medium | Low-Medium |
| Best use case | Privacy-conscious orgs, custom tool logic | Fast setup, standard support agent workflows | Multi-step automation, existing Pipedream users | Non-technical teams, simple actions |
Architecture and Data Flow
Understanding what happens during an MCP tool call helps you debug issues and design secure deployments.
┌─────────────────────────────────────────────────────────────┐
│ MCP Client Layer │
│ ┌─────────────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ Claude Desktop │ │ Cursor │ │ Claude Code │ │
│ └────────┬────────┘ └──────┬────────┘ └──────┬───────┘ │
└───────────┼──────────────────┼──────────────────┼──────────┘
│ JSON-RPC 2.0 │ (stdio/SSE/HTTP)│
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Freshdesk MCP Server │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Tool Registry │ │
│ │ - freshdesk_create_ticket │ │
│ │ - freshdesk_get_ticket │ │
│ │ - freshdesk_list_tickets │ │
│ │ - freshdesk_update_ticket │ │
│ │ - freshdesk_add_note │ │
│ │ - freshdesk_list_contacts │ │
│ └──────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌──────────────────▼───────────────────────────────────┐ │
│ │ Request Handler │ │
│ │ - Input validation │ │
│ │ - Auth header injection │ │
│ │ - Rate limit handling │ │
│ │ - Error normalization │ │
│ └──────────────────┬───────────────────────────────────┘ │
└─────────────────────┼───────────────────────────────────────┘
│ HTTPS REST API calls
▼
┌─────────────────────────────────────────────────────────────┐
│ Freshdesk REST API v2 │
│ https://{your-domain}.freshdesk.com/api/v2 │
│ │
│ /tickets /contacts /agents /groups /conversations │
└─────────────────────────────────────────────────────────────┘
What happens during a tool call:
- The AI agent (Claude, Cursor) decides it needs to call
freshdesk_create_ticketbased on user input - The MCP client sends a
tools/callJSON-RPC request to the MCP server with the tool name and arguments - The MCP server validates inputs, constructs a Freshdesk API request with the API key in the Authorization header
- Freshdesk REST API v2 returns a response (201 Created with ticket data, or an error)
- The MCP server normalizes the response into an MCP
CallToolResultand returns it to the client - The AI agent uses the result to continue reasoning or present the outcome to the user
For managed platforms (Composio, Pipedream), steps 3–5 happen in the platform's cloud infrastructure rather than on your machine.
Freshdesk MCP Server vs Freshdesk API
This is a genuine architectural decision, not just a preference. Here's when each approach makes sense:
Use MCP when:
- An AI agent needs to decide at runtime which Freshdesk operations to perform. For example, Claude reading a customer email and deciding whether to create a ticket, look up an existing one, or escalate — all based on content.
- You're building support agent assistants where the agent interprets natural language and translates it into structured Freshdesk operations.
- You need tool composition: the agent might call
freshdesk_get_ticket, thenfreshdesk_list_contacts, thenfreshdesk_add_notein sequence based on what it finds. - You want to expose Freshdesk to a non-technical user through an AI interface ("show me all open P1 tickets assigned to the infrastructure team").
Use the Freshdesk REST API directly when:
- Your logic is fully deterministic — you always create a ticket with fixed fields, triggered by a specific event
- You're building a traditional integration (webhook → process → API call) with no LLM decision-making
- You need maximum performance — direct API calls have lower latency than routing through an MCP server
- You require fine-grained control over pagination, custom fields, or API features not yet exposed by a given MCP implementation
- Compliance requirements prohibit routing customer data through a third-party MCP platform
Use both when:
A common production pattern is using the Freshdesk REST API directly for high-volume, automated, deterministic operations (syncing data, webhook-triggered ticket creation), while using MCP for the AI-assisted layer where an agent needs contextual judgment (summarization, triage decisions, drafting responses).
Tutorial: Setting Up Freshdesk MCP with Composio
This tutorial uses Composio as the MCP implementation. It's selected because:
- The tool set is documented publicly
- Claude Desktop and Cursor configuration is documented
- Setup is achievable in under 30 minutes without writing a custom server
- It's actively maintained by the Composio team
If you need full data sovereignty or custom tool logic, use a self-hosted community implementation — the second half of this section covers what you'd need to adapt.
Prerequisites
Before starting:
- Freshdesk account with Admin role access (you need API key access)
- Freshdesk subdomain — e.g.,
yourcompany.freshdesk.com - Composio account — sign up at composio.dev
- Node.js 18+ installed locally (for the Composio CLI)
- Claude Desktop, Cursor, or another MCP-compatible client
- Your Freshdesk API key (Admin profile → API Key)
Step 1: Get Your Freshdesk API Key
Freshdesk authenticates REST API v2 requests using HTTP Basic Auth where:
- Username = your API key
- Password = any string (commonly
X)
To find your API key:
- Log into Freshdesk as an admin
- Click your avatar → Profile Settings
- Scroll to Your API Key in the right panel
- Copy the key — treat it like a password
Verify it works:
curl -u YOUR_API_KEY:X \
-H "Content-Type: application/json" \
https://yourcompany.freshdesk.com/api/v2/tickets?per_page=1
A 200 OK with ticket JSON confirms the key and domain are correct.
Step 2: Install the Composio CLI
npm install -g composio-core
Verify installation:
composio --version
Step 3: Authenticate with Composio
composio login
This opens a browser window for OAuth authentication with your Composio account.
Step 4: Connect Freshdesk to Composio
composio add freshdesk
The CLI will prompt for your Freshdesk domain and API key. Enter:
- Domain:
yourcompany(just the subdomain, not the full URL) - API Key: the key from Step 1
Composio stores these credentials in its vault and validates the connection by making a test API call. You should see a success message confirming the integration is active.
Verify the connection:
composio connections list
You should see freshdesk in the list with status active.
Step 5: List Available Freshdesk Tools
composio tools list --app freshdesk
This shows the Freshdesk tools Composio exposes as MCP tools. Based on Composio's documented Freshdesk integration, expect tools such as:
FRESHDESK_CREATE_TICKET— Create a new support ticketFRESHDESK_GET_TICKET— Retrieve a ticket by IDFRESHDESK_LIST_TICKETS— List tickets with optional filtersFRESHDESK_UPDATE_TICKET— Update ticket fields (status, priority, assignee, tags)FRESHDESK_ADD_NOTE— Add a private or public note to a ticketFRESHDESK_LIST_CONTACTS— List contacts with optional searchFRESHDESK_GET_CONTACT— Get a specific contact by IDFRESHDESK_CREATE_CONTACT— Create a new contact
Important: Always verify available tools using the CLI against the version you've installed. Composio updates its tool set, and this list may change. Do not assume tools are available without confirming with
composio tools list.
Step 6: Configure Claude Desktop
Open your Claude Desktop configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add the Composio MCP server block:
{
"mcpServers": {
"freshdesk": {
"command": "composio",
"args": ["mcp", "start", "--apps", "freshdesk"],
"env": {}
}
}
}
If you haven't globally installed Composio or prefer an explicit path:
{
"mcpServers": {
"freshdesk": {
"command": "npx",
"args": ["composio-core", "mcp", "start", "--apps", "freshdesk"],
"env": {}
}
}
}
Restart Claude Desktop. Open a new conversation and type:
"List my 5 most recent open Freshdesk tickets."
Claude should invoke the FRESHDESK_LIST_TICKETS tool and return results from your Freshdesk account.
Step 7: Configure Cursor
Create or edit ~/.cursor/mcp.json:
{
"mcpServers": {
"freshdesk": {
"command": "composio",
"args": ["mcp", "start", "--apps", "freshdesk"]
}
}
}
In Cursor settings, navigate to Features → MCP and confirm the server is listed and shows a green status indicator.
Step 8: Configure Claude Code
For Claude Code (Anthropic's CLI-based coding agent), add the MCP server via:
claude mcp add freshdesk -- composio mcp start --apps freshdesk
Verify:
claude mcp list
Self-Hosted Community Implementation (Reference)
If Composio doesn't fit your requirements — data sovereignty, custom tools, no third-party in the data path — here's what a self-hosted approach looks like using the MCP SDK.
Minimal TypeScript Server Structure
// freshdesk-mcp-server/src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const FRESHDESK_DOMAIN = process.env.FRESHDESK_DOMAIN; // e.g., "yourcompany"
const FRESHDESK_API_KEY = process.env.FRESHDESK_API_KEY;
if (!FRESHDESK_DOMAIN || !FRESHDESK_API_KEY) {
throw new Error("FRESHDESK_DOMAIN and FRESHDESK_API_KEY must be set");
}
const BASE_URL = `https://${FRESHDESK_DOMAIN}.freshdesk.com/api/v2`;
const AUTH_HEADER = `Basic ${Buffer.from(`${FRESHDESK_API_KEY}:X`).toString("base64")}`;
async function freshdeskRequest(path: string, method = "GET", body?: object) {
const response = await fetch(`${BASE_URL}${path}`, {
method,
headers: {
Authorization: AUTH_HEADER,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || "60";
throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);
}
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(
`Freshdesk API error ${response.status}: ${JSON.stringify(error)}`
);
}
if (response.status === 204) return null;
return response.json();
}
const server = new Server(
{ name: "freshdesk-mcp-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "freshdesk_get_ticket",
description: "Retrieve a Freshdesk ticket by its ID",
inputSchema: {
type: "object",
properties: {
ticket_id: { type: "number", description: "The Freshdesk ticket ID" },
},
required: ["ticket_id"],
},
},
{
name: "freshdesk_list_tickets",
description: "List Freshdesk tickets with optional filters",
inputSchema: {
type: "object",
properties: {
status: {
type: "number",
description: "Filter by status: 2=open, 3=pending, 4=resolved, 5=closed",
},
priority: {
type: "number",
description: "Filter by priority: 1=low, 2=medium, 3=high, 4=urgent",
},
per_page: {
type: "number",
description: "Results per page (max 100)",
default: 30,
},
},
},
},
{
name: "freshdesk_create_ticket",
description: "Create a new Freshdesk support ticket",
inputSchema: {
type: "object",
properties: {
subject: { type: "string", description: "Ticket subject" },
description: { type: "string", description: "Ticket description (HTML allowed)" },
email: { type: "string", description: "Requester email address" },
priority: {
type: "number",
description: "Priority: 1=low, 2=medium, 3=high, 4=urgent",
},
status: {
type: "number",
description: "Status: 2=open, 3=pending, 4=resolved",
},
tags: {
type: "array",
items: { type: "string" },
description: "Tags to apply to the ticket",
},
},
required: ["subject", "description", "email"],
},
},
{
name: "freshdesk_update_ticket",
description: "Update fields on an existing Freshdesk ticket",
inputSchema: {
type: "object",
properties: {
ticket_id: { type: "number", description: "Ticket ID to update" },
status: { type: "number", description: "New status value" },
priority: { type: "number", description: "New priority value" },
assignee_id: { type: "number", description: "Agent ID to assign to" },
tags: { type: "array", items: { type: "string" } },
},
required: ["ticket_id"],
},
},
{
name: "freshdesk_add_note",
description: "Add a note or reply to a Freshdesk ticket",
inputSchema: {
type: "object",
properties: {
ticket_id: { type: "number", description: "Ticket ID" },
body: { type: "string", description: "Note content (HTML allowed)" },
private: {
type: "boolean",
description: "True for private note, false for public reply",
default: true,
},
},
required: ["ticket_id", "body"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
let result;
switch (name) {
case "freshdesk_get_ticket":
result = await freshdeskRequest(`/tickets/${args.ticket_id}`);
break;
case "freshdesk_list_tickets": {
const params = new URLSearchParams();
if (args.status) params.set("status", String(args.status));
if (args.priority) params.set("priority", String(args.priority));
params.set("per_page", String(args.per_page || 30));
result = await freshdeskRequest(`/tickets?${params}`);
break;
}
case "freshdesk_create_ticket":
result = await freshdeskRequest("/tickets", "POST", args);
break;
case "freshdesk_update_ticket": {
const { ticket_id, ...updateFields } = args;
result = await freshdeskRequest(`/tickets/${ticket_id}`, "PUT", updateFields);
break;
}
case "freshdesk_add_note":
result = await freshdeskRequest(
`/tickets/${args.ticket_id}/notes`,
"POST",
{ body: args.body, private: args.private ?? true }
);
break;
default:
throw new Error(`Unknown tool: ${name}`);
}
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Freshdesk MCP Server running on stdio");
}
main().catch(console.error);
Running the Self-Hosted Server
# Install dependencies
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node
# Build
npx tsc
# Run with environment variables
FRESHDESK_DOMAIN=yourcompany FRESHDESK_API_KEY=your_api_key node dist/index.js
Claude Desktop Config (Self-Hosted)
{
"mcpServers": {
"freshdesk": {
"command": "node",
"args": ["/absolute/path/to/freshdesk-mcp-server/dist/index.js"],
"env": {
"FRESHDESK_DOMAIN": "yourcompany",
"FRESHDESK_API_KEY": "your_api_key_here"
}
}
}
}
Security note: Avoid storing the actual API key in the config file for production. Use environment variables set at the OS level, or use a secrets manager and have the server fetch credentials at startup.
Freshdesk API Rate Limits and Production Behavior
Freshdesk REST API v2 enforces per-account rate limits. These are documented in the Freshdesk API Rate Limiting docs:
| Plan | Rate Limit |
|---|---|
| Sprout (free) | ~100 requests/minute |
| Blossom, Garden | 50–100 requests/minute |
| Estate | Higher limits (consult Freshdesk support) |
| Forest | Negotiated enterprise limits |
When the limit is exceeded, Freshdesk returns HTTP 429 Too Many Requests with a Retry-After header indicating when to retry.
In practice for MCP usage: An AI agent doing multi-step reasoning (get ticket → get contact → get ticket history → add note) may consume 4+ API calls in seconds. For a support team with heavy volume and multiple concurrent AI agents, you can hit rate limits faster than expected.
Mitigation strategies:
- Implement exponential backoff in your MCP server's request handler
- Cache frequently-read data (ticket status, contact info) with a short TTL
- Batch ticket list calls where possible using
per_pageparameter - Monitor API call volume with server-side logging
Available Tools: What You Can Actually Do
This table reflects what a reasonable Freshdesk MCP implementation exposes, based on the Freshdesk REST API v2's documented capabilities. Specific implementations may expose more or fewer tools.
| Tool | Freshdesk API Endpoint | Key Parameters | Notes |
|---|---|---|---|
| Get ticket | GET /api/v2/tickets/{id} | ticket_id | Returns full ticket with all fields |
| List tickets | GET /api/v2/tickets | status, priority, assignee_id, per_page | Max 100 per page; supports filter queries |
| Create ticket | POST /api/v2/tickets | subject, description, email, priority, status | email is used to match/create contact |
| Update ticket | PUT /api/v2/tickets/{id} | status, priority, assignee_id, group_id, tags | Partial updates supported |
| Add note | POST /api/v2/tickets/{id}/notes | body, private | private=false sends public reply to customer |
| List contacts | GET /api/v2/contacts | email, mobile, page | Search by email to find customer history |
| Get contact | GET /api/v2/contacts/{id} | contact_id | Returns full contact profile |
| Create contact | POST /api/v2/contacts | name, email, phone | Creates a new contact record |
| List agents | GET /api/v2/agents | — | Used for assigning tickets |
| List groups | GET /api/v2/groups | — | Used for ticket routing |
Operations not typically exposed in community MCP implementations (but available via the raw API):
- Custom field management
- SLA policies
- Solution articles (Freshdesk Knowledge Base)
- Satisfaction ratings
- Ticket merging
- Business hours configuration
If you need these, a self-hosted implementation where you control the tool registry is the right choice.
Real-World Freshdesk MCP Workflows
These are practical workflows you can implement with a properly configured Freshdesk MCP Server.
1. AI-Powered Ticket Triage
Problem: High-volume support queues with inconsistent priority assignment.
Workflow:
User message: "Triage unassigned open tickets and set priorities based on urgency language"
Agent calls:
1. freshdesk_list_tickets(status=2, per_page=50) → returns 50 open tickets
2. For each ticket, reads subject + description
3. Classifies urgency: "down", "outage", "critical" → priority=4 (urgent)
4. freshdesk_update_ticket(ticket_id=..., priority=4, tags=["auto-triaged"])
5. Returns summary of actions taken
This replaces a human doing manual triage during off-hours. The AI applies consistent urgency rules and adds a tag so the team knows which tickets were auto-triaged.
2. Automatic Ticket Summarization
Problem: Agents spend time reading long ticket threads before responding.
Workflow:
User (agent) message: "Summarize ticket #4821 for me"
Agent calls:
1. freshdesk_get_ticket(ticket_id=4821) → full ticket data including conversation thread
2. LLM reads subject, description, and all notes/replies
3. Returns: "Ticket #4821: Customer reporting login failures since 2pm EST.
3 replies exchanged. Customer has already reset password twice.
Current status: Pending. Last reply was from customer asking for escalation."
No additional API calls needed — the ticket object from the Freshdesk API includes the full conversation when fetched with include=conversations.
3. Customer History Lookup
Problem: Support agents need context about a customer before responding.
Workflow:
User: "What's the history for customer john@example.com?"
Agent calls:
1. freshdesk_list_contacts(email="john@example.com") → finds contact ID
2. freshdesk_list_tickets(requester_id=contact_id, per_page=20) → recent tickets
3. Returns: "John Smith has submitted 7 tickets in the past 6 months.
4 resolved, 2 closed, 1 currently open (#4821).
Common issues: login problems, billing questions."
4. Escalation Detection and Routing
Problem: Escalation signals (frustrated language, VIP customers) get missed in high-volume queues.
Workflow:
Scheduled agent run (every 15 minutes):
1. freshdesk_list_tickets(status=2, per_page=100) → all open tickets
2. LLM scans for escalation signals:
- Keywords: "unacceptable", "cancel", "legal", "management", "lawsuit"
- Contact is tagged VIP in Freshdesk
- Ticket age > 48 hours with no agent reply
3. For flagged tickets:
freshdesk_update_ticket(ticket_id=..., priority=4, group_id=escalation_team_id)
freshdesk_add_note(ticket_id=..., body="Auto-escalated: [reason]", private=true)
5. Knowledge-Assisted Support Agent
Problem: Agents need to draft accurate replies without deep product knowledge.
Workflow:
User (support agent): "Draft a reply for ticket #5102"
Agent calls:
1. freshdesk_get_ticket(ticket_id=5102) → reads the customer's issue
2. LLM cross-references internal knowledge base (via separate MCP tool or RAG)
3. Drafts a reply based on the ticket content + knowledge base answer
4. Agent reviews and approves
5. freshdesk_add_note(ticket_id=5102, body="[drafted reply]", private=false)
→ sends reply to customer
6. Multi-Agent Customer Support System
For larger teams, you can build a multi-agent pipeline:
Incoming ticket
↓
[Classifier Agent] → categorizes ticket type, sets tags
↓
[Router Agent] → assigns to appropriate group based on category
↓
[Responder Agent] → drafts initial acknowledgment reply
↓
[Escalation Monitor] → runs periodically, checks for SLA breach risk
Each agent uses the same Freshdesk MCP Server but different system prompts and tool access patterns. The classifier might only need freshdesk_get_ticket and freshdesk_update_ticket, while the responder needs freshdesk_add_note as well.
Security and Privacy Considerations
Customer support data is sensitive — names, emails, billing information, support conversations. Your Freshdesk MCP setup needs to account for this.
Credential Management
Development:
# Use .env files locally, never commit them
echo "FRESHDESK_API_KEY=your_key_here" >> .env
echo ".env" >> .gitignore
Production:
# AWS Systems Manager Parameter Store
aws ssm put-parameter \
--name "/freshdesk-mcp/api-key" \
--value "your_api_key" \
--type "SecureString"
# Retrieve at startup
export FRESHDESK_API_KEY=$(aws ssm get-parameter \
--name "/freshdesk-mcp/api-key" \
--with-decryption \
--query "Parameter.Value" \
--output text)
Least-Privilege API Access
Freshdesk's API key inherits the permissions of the user account it belongs to. Best practice:
- Create a dedicated Freshdesk agent account for the MCP integration (e.g.,
mcp-agent@yourcompany.com) - Assign this account only the roles needed: Agent or a custom role with only the permissions your MCP tools require
- Avoid using admin API keys — an admin key exposed in a security incident gives full Freshdesk access
- Note: Freshdesk API keys are per-user, not per-application. There is no OAuth client credential flow for server-to-server use in the standard Freshdesk API v2 — API key is the standard approach
Data Residency with Managed Platforms
If using Composio, Pipedream, or Zapier:
- Customer ticket data (names, emails, conversation content) transits through the platform's infrastructure when tool calls are processed
- Review the platform's data processing agreement and privacy policy before connecting production Freshdesk data
- For GDPR, HIPAA, or SOC 2-sensitive environments, a self-hosted community implementation that keeps data on your infrastructure is preferable
Tool-Level Authorization
Consider implementing tool-level authorization in your MCP server — for example, restricting which tools different AI agents can access:
// In your CallToolRequestSchema handler
const RESTRICTED_TOOLS = ["freshdesk_add_note", "freshdesk_update_ticket"];
const AGENT_ROLE = process.env.AGENT_ROLE; // "read-only" or "read-write"
if (AGENT_ROLE === "read-only" && RESTRICTED_TOOLS.includes(name)) {
return {
content: [{ type: "text", text: "Error: This agent has read-only access" }],
isError: true,
};
}
Common Errors and Fixes
Error: 401 Unauthorized
Cause: Invalid API key, wrong base64 encoding, or wrong Freshdesk domain.
Fix:
# Verify manually
curl -u YOUR_API_KEY:X https://yourcompany.freshdesk.com/api/v2/tickets?per_page=1
# Check domain — should NOT include https:// or .freshdesk.com
# Correct: yourcompany
# Wrong: https://yourcompany.freshdesk.com
Error: 403 Forbidden
Cause: The API key belongs to an agent without permission for the requested operation (e.g., an agent trying to access admin-only endpoints).
Fix: Use an API key from an account with appropriate permissions. For ticket operations, Agent role is sufficient. For agent/group management, Admin role is needed.
Error: 429 Too Many Requests
Cause: Rate limit exceeded. Common when an AI agent makes many rapid sequential calls.
Fix:
// Add retry logic in your MCP server
async function freshdeskRequestWithRetry(path: string, method = "GET", body?: object, retries = 3) {
for (let attempt = 0; attempt < retries; attempt++) {
const response = await fetch(...);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") || "60");
if (attempt < retries - 1) {
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
}
// ... rest of handler
}
}
Error: MCP server not appearing in Claude Desktop
Cause: JSON syntax error in claude_desktop_config.json, wrong command path, or Claude Desktop not restarted.
Fix:
# Validate JSON
python3 -m json.tool ~/Library/Application\ Support/Claude/claude_desktop_config.json
# Check command exists
which composio
# Check logs
tail -f ~/Library/Logs/Claude/mcp*.log
Error: Tool not found: freshdesk_create_ticket
Cause: Tool name mismatch between what the server advertises and what the client calls. Different implementations use different naming conventions.
Fix: List available tools explicitly:
# With Composio
composio tools list --app freshdesk
# With self-hosted server, use MCP Inspector
npx @modelcontextprotocol/inspector node dist/index.js
Error: 404 Not Found on ticket operations
Cause: Wrong ticket ID, or the ticket was deleted/merged.
Fix: Verify the ticket ID exists in Freshdesk. Check if the ticket was merged (Freshdesk returns 404 for merged tickets accessed by their original ID).
Production Deployment Considerations
Monitoring
For a self-hosted MCP server in production:
// Add structured logging for observability
import { createLogger, transports, format } from "winston";
const logger = createLogger({
format: format.json(),
transports: [new transports.Console()],
});
// Log every tool call
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const start = Date.now();
const { name, arguments: args } = request.params;
try {
const result = await handleTool(name, args);
logger.info("tool_call_success", {
tool: name,
duration_ms: Date.now() - start,
});
return result;
} catch (error) {
logger.error("tool_call_error", {
tool: name,
error: (error as Error).message,
duration_ms: Date.now() - start,
});
throw error;
}
});
Send these logs to your observability platform (Datadog, CloudWatch, Grafana Loki) and set alerts for:
- Error rate > 5% over a 5-minute window
- p99 latency > 5 seconds
- Rate limit errors (HTTP 429)
Deployment Options for Self-Hosted Servers
| Option | Transport | Best for |
|---|---|---|
| Local process | stdio | Developer workstations, Claude Desktop |
| Docker container | SSE or HTTP | Team-shared deployments, CI/CD agents |
| AWS Lambda | HTTP (stateless) | Serverless, low-cost, auto-scaling |
| Kubernetes pod | SSE or HTTP | Enterprise, high-availability requirements |
For a deep dive on production MCP deployment patterns, see the MCPForge production deployment guide.
Health Checks
Add a simple health check that verifies the Freshdesk connection is alive:
// Startup health check
async function healthCheck() {
try {
await freshdeskRequest("/tickets?per_page=1");
console.error("Freshdesk connection: OK");
} catch (error) {
console.error("Freshdesk connection: FAILED", error);
process.exit(1);
}
}
// Run before starting server
healthCheck().then(() => {
const transport = new StdioServerTransport();
server.connect(transport);
});
Verifying Your MCP Server Before Production
Before connecting a Freshdesk MCP Server to production AI agents, verify its tool definitions are correct, its error handling is robust, and its tool descriptions are accurate enough for LLMs to use correctly.
The MCPForge Verify tool can inspect your MCP server's tool manifest and flag issues like missing required parameters, ambiguous descriptions, or tools that don't follow MCP specification conventions. For published community implementations, check the MCPForge verified directory to see if the server has been reviewed.
Limitations to Know
Freshdesk API limitations that affect MCP:
- No real-time push: Freshdesk doesn't push events to your MCP server. You can only poll. For webhook-style triggers, use Freshdesk webhooks + a separate event processor.
- Attachment handling: Uploading attachments requires
multipart/form-datawhich is more complex to expose as an MCP tool and not supported in most community implementations - Custom objects: Freshdesk's custom objects (CRM-style records) are available in the API but rarely exposed in community MCP implementations
- Marketplace apps data: Data managed by Freshdesk Marketplace apps is generally not accessible via the core REST API v2
MCP-specific limitations:
- No subscriptions: MCP doesn't natively support server-sent events back to the client (notifications are one-directional). You can't have the MCP server push a notification when a new ticket arrives.
- Context window limits: If you fetch many tickets with full conversation history, the JSON response can be large. Implement pagination and truncation in your MCP server.
Quick Reference: Freshdesk Status and Priority Codes
These numeric codes are what you'll use in MCP tool parameters:
| Status | Code | Priority | Code |
|---|---|---|---|
| Open | 2 | Low | 1 |
| Pending | 3 | Medium | 2 |
| Resolved | 4 | High | 3 |
| Closed | 5 | Urgent | 4 |
Key Takeaways
- No official Freshdesk MCP Server exists from Freshworks — you're choosing between community self-hosted implementations and managed platforms
- Composio offers the fastest path to a working Freshdesk MCP setup with documented tools and multi-client support
- Self-hosted implementations give you full control, data sovereignty, and customizability at the cost of maintenance responsibility
- MCP is the right choice when an AI agent needs runtime decision-making about which Freshdesk operations to perform; use the API directly for deterministic integrations
- Rate limits matter — design for Retry-After handling from day one, not as an afterthought
- Credential security requires dedicated low-privilege agent accounts, environment variable injection, and secrets management — never plain-text API keys in committed config files
- Customer data sensitivity means you need to carefully evaluate whether a managed MCP platform is appropriate for your compliance environment