mcp-freshservice
Integrates with the Freshservice REST API v2 to let an AI assistant work with IT service management data: tickets, replies, notes and conversations, time entries, tasks and approvals; requesters, agents and groups; the service catalog and knowledge base (categories, folders, articles, search); change, problem and release records; assets, asset types and canned responses; and organisation data such as workspaces, departments, locations, vendors and products. Supports query-language filtering, pagination, read-only mode, opt-in toolsets and local attachment uploads from allow-listed folders.
mcp-freshservice
A Model Context Protocol server for the Freshservice REST API v2. It lets an AI assistant work with tickets, changes, problems, releases, assets, requesters, agents, groups, the service catalog and the knowledge base.
Forked from effytech/freshservice_mcp and reworked for v2 (see CHANGELOG.md for breaking changes).
Quick start
You need a Freshservice API key (Profile Settings → API key) and your domain.
uvx mcp-freshservice --versionClaude Desktop / any MCP client (claude_desktop_config.json):
{
"mcpServers": {
"freshservice": {
"command": "uvx",
"args": ["mcp-freshservice"],
"env": {
"FRESHSERVICE_DOMAIN": "yourcompany.freshservice.com",
"FRESHSERVICE_APIKEY": "<your-api-key>"
}
}
}
}Claude Code: claude mcp add freshservice -e FRESHSERVICE_DOMAIN=yourcompany.freshservice.com -e FRESHSERVICE_APIKEY=... -- uvx mcp-freshservice
Related MCP server: Freshservice MCP Server
Configuration
Variable | Required | Description |
| yes |
|
| yes | API key of the agent the server acts as. Permissions follow that agent |
| no | Default |
| no |
|
| no | Which toolsets to expose: names, |
| no | HTTP timeout in seconds (default 30) |
| no | Folder(s) the server may read attachments from ( |
| no | Retries after a rate limit (429) or a transient 502/503/504 on reads (default 3, |
| no |
|
The same switches exist as flags: --read-only, --toolsets tickets,solutions, --transport.
Choosing tools
The server can expose 204 tools (full list in TOOLS.md), but some clients cap the tools per agent (for example 128) and models choose better from a shorter list. So by default only a 76-tool core set is enabled; everything else is opt-in by toolset.
uvx mcp-freshservice --list-toolsets # see what's available
uvx mcp-freshservice --toolsets default,changes,assets # core set + changes + assets
uvx mcp-freshservice --toolsets all # everything
uvx mcp-freshservice --toolsets tickets,solutions --read-onlyThe same works with FRESHSERVICE_TOOLSETS=default,changes in the client's env. A list of plain names is
exact (only those toolsets); add default to keep the core set.
Toolset | Default | Tools | Covers |
| yes | 17 | tickets, replies and notes (with attachments), conversations, child tickets, requested items |
| yes | 5 | time logged on tickets |
| 5 | ticket tasks | |
| 9 | service-request approval groups and approvals | |
| yes | 9 | requesters, fields, reactivate, convert to agent |
| yes | 9 | agents, fields, roles |
| yes | 13 | agent groups, requester groups and membership |
| yes | 18 | knowledge-base categories, folders, articles, search |
| yes | 5 | browse/search items, place service requests |
| 9 | change requests and planning fields | |
| 5 each | notes, tasks, time entries on changes | |
| 9 | change approval groups, chains, approvals | |
| 8, 7 | problem and release records | |
| 5 each | notes, tasks, time entries | |
| 16 | assets, asset types, canned responses | |
| 20 | workspaces, departments, locations, vendors, products |
Every tool is annotated read-only / destructive so clients can auto-approve safely.
Conventions
Single records come back as Freshservice returns them:
{"ticket": {...}}.Lists return one page plus
pagination: {page, per_page, next_page, has_more}.per_pageis at most 100.Errors are real MCP tool errors (
isError: true) carrying Freshservice's message and field errors, with hints for 401/403/404/429.Filtering: see Query syntax.
Numeric codes: ticket status 2 Open, 3 Pending, 4 Resolved, 5 Closed; priority 1 Low … 4 Urgent; source 1 Email, 2 Portal, 3 Phone, 4 Chat … (see
list_ticket_sourcesfor the full list).Attachments:
send_ticket_reply,create_ticket_note,create_ticketandupdate_ticket_conversationaccept local file paths (max 15 files / 40 MB), but only from folders listed inFRESHSERVICE_ATTACHMENT_DIRS. Until you set it, attachments are refused, so a prompt-injected path can't upload an arbitrary local file.
Example prompts: "Show ticket 12031 with its conversations and resolution notes", "How much time was logged on ticket 8?", "List open P1 incidents unassigned for a day", "Search the KB for printer setup".
Query syntax
filter_tickets, filter_changes, filter_requesters, filter_agents and the query / filter / search
arguments of the list tools use Freshservice's query language. Freshservice requires the whole query to be
enclosed in a pair of double quotes (without them the API answers 500), so the server adds them for you;
you can pass status:3 or "status:3" and get the same request.
Rule | Example |
Fields are snake_case and case-sensitive (custom fields by API name) |
|
Numbers bare, strings and dates in single quotes |
|
|
|
|
|
|
|
Starts-with (requesters and agents only) |
|
Max 512 characters (checked locally); filter results are 30 per page |
Changes: "status:3" awaiting approval, "approval_status:1" approved,
"approval_status:1 AND status:<5" approved and not closed (status 6 is Closed, and :< includes its boundary, so <6 would include closed changes), "status:3 AND priority:1". approval_status
is 0 Requested, 1 Approved, 2 Rejected, 3 Not requested. A change view and query cannot be combined.
Not every endpoint supports every field: departments and locations filter on name only; asset search
takes one field (name, asset_tag, serial_number, ...); releases only have predefined filter_name
values. The per-tool descriptions list the supported fields. Filtered requester/agent pages are capped at 40;
changes to users can take a few minutes to be indexed.
Rate limits
Freshservice limits requests per minute. When it answers HTTP 429 the server waits for the Retry-After
time (up to 20 seconds) and tries again, up to FRESHSERVICE_MAX_RETRIES times; transient 502/503/504 errors
on reads are retried the same way. If the wait would be longer than 20 seconds, or retries run out, the tool
reports the rate-limit error instead. Writes are only ever retried after a 429, because a request that was
rate-limited was not processed.
Running remotely
MCP_TRANSPORT=streamable-http MCP_PORT=8000 uvx mcp-freshserviceThe server is stateless over streamable HTTP and serves /mcp. It has no authentication of its own:
run it behind a gateway/reverse proxy that authenticates callers, because everyone who can reach it acts
with the configured API key.
Development
uv sync
uv run pytest # offline, mocks the Freshservice API
uv run ruff check .To check the server against a real Freshservice account (read-only by default; never prints your key):
uv run python scripts/live_check.py # the core tools, read-only
uv run python scripts/live_check.py --sweep # every read-only tool of every toolsetThe sweep calls each read-only tool once, using real IDs it finds along the way. A wrong endpoint shows up as a failure; an empty list or a 403 (module not on your plan, or no permission) is a skip.
When you point an MCP client at a local checkout with uvx --from <folder> mcp-freshservice, uvx reuses a
cached build and will not notice your code changes (--refresh does not help). Add --no-cache to the uvx
arguments while developing, e.g. "args": ["--no-cache", "--from", "G:/path/to/mcp-freshservice", "mcp-freshservice"].
Tests call tools through FastMCP's real call_tool path against a mocked API, so output-schema problems
(like the old get_ticket_by_id failure) are caught without credentials. Each tool module lives in
src/mcp_freshservice/tools/; add tools with @ts.tool(title, kind) and they are registered, filtered and
annotated automatically.
Releasing
Bump
versioninpyproject.tomland add a## <version>section toCHANGELOG.md.Merge to
main, then create a GitHub release taggedv<version>.The publish workflow first checks that the tag, the package version and the changelog agree, then runs lint and the tests, and only then uploads to PyPI (after your approval on the
pypienvironment).
python scripts/check_release.py v<version> runs the first check locally. PyPI never accepts a version twice,
so the check exists to fail before anything is uploaded.
License
MIT, see LICENSE.
Available Tools
76 toolsadd_requester_to_groupAdd requester to groupBIdempotent
Add a requester to a manual requester group.
Inputs: requester_id (required), requester_group_id (required). Returns: object
convert_requester_to_agentConvert requester to agentCIdempotent
Convert a requester into an occasional agent.
Inputs: requester_id (required). Returns: object
create_agentCreate agentB
Create an agent. Assign at least one role so the agent can sign in.
Inputs: email (required), first_name (required), roles, language, +11 more. Returns: object
create_child_ticketCreate child ticketC
Create a child ticket under an existing parent ticket.
Inputs: subject (required), ticket_id (required), description (required), email, +7 more. Returns: object
create_groupCreate agent groupC
Create an agent group.
Inputs: name (required), members, agent_ids, group_type, +6 more. Returns: object
create_requesterCreate requesterB
Create a requester. One of primary_email, work_phone_number or mobile_phone_number is required.
Inputs: first_name (required), address, language, job_title, +13 more. Returns: object
create_requester_groupCreate requester groupC
Create a requester group.
Inputs: name (required), type, conditions, description. Returns: object
create_service_requestCreate service requestC
Place a service request for a catalog item.
Inputs: email (required), display_id (required), quantity, child_items, +2 more. Returns: object
create_solution_articleCreate solution articleB
Create a knowledge-base article (a draft unless status is 2).
Inputs: title (required), folder_id (required), description (required), tags, +4 more. Returns: object
create_solution_categoryCreate solution categoryC
Create a knowledge-base category.
Inputs: name (required), description, workspace_id. Returns: object
create_solution_folderCreate solution folderC
Create a knowledge-base folder.
Inputs: name (required), category_id (required), group_ids, visibility, +4 more. Returns: object
create_ticketCreate ticketA
Create an incident or service-request ticket. Some ticket forms make fields such as group_id, responder_id, category or custom fields mandatory; check get_ticket_fields when the API reports missing_field.
Inputs: subject (required), description (required), name, tags, +22 more. Returns: object
create_ticket_noteAdd ticket noteB
Add a note to a ticket (private by default).
Inputs: body (required), ticket_id (required), private, user_id, +3 more. Returns: object
create_ticket_time_entryLog ticket timeC
Log time against a ticket.
Inputs: agent_id (required), ticket_id (required), note, task_id, +5 more. Returns: object
deactivate_agentDeactivate agentADestructiveIdempotent
Deactivate an agent (they become a requester; reversible with reactivate_agent).
Inputs: agent_id (required). Returns: object
deactivate_requesterDeactivate requesterADestructiveIdempotent
Deactivate a requester (reversible with reactivate_requester).
Inputs: requester_id (required). Returns: object
delete_groupDelete agent groupCDestructiveIdempotent
Delete an agent group.
Inputs: group_id (required). Returns: object
delete_requester_groupDelete requester groupCDestructiveIdempotent
Delete a requester group.
Inputs: requester_group_id (required). Returns: object
delete_solution_articleDelete solution articleADestructiveIdempotent
Move an article to trash.
Inputs: article_id (required). Returns: object
delete_solution_categoryDelete solution categoryADestructiveIdempotent
Move a category (and its folders and articles) to trash.
Inputs: category_id (required). Returns: object
delete_solution_folderDelete solution folderADestructiveIdempotent
Move a folder (and its articles) to trash.
Inputs: folder_id (required). Returns: object
delete_ticketDelete ticketADestructiveIdempotent
Move a ticket to trash. It can be brought back with restore_ticket.
Inputs: ticket_id (required). Returns: object
delete_ticket_conversationDelete ticket conversationBDestructiveIdempotent
Delete a note or reply from a ticket.
Inputs: conversation_id (required). Returns: object
delete_ticket_time_entryDelete ticket time entryCDestructiveIdempotent
Delete a time entry on a ticket.
Inputs: ticket_id (required), time_entry_id (required). Returns: object
filter_agentsFilter agentsARead-onlyIdempotent
Search agents (30 per page, newest first; results cannot be sorted).
Fields: first_name, last_name, name, job_title, email, work_phone_number, mobile_phone_number, department_id, reporting_manager_id, time_zone, language, location_id, created_at, updated_at, external_id and custom fields. The ~ operator does "starts with" on first_name, last_name, name, email, mobile_phone_number and work_phone_number. Changes can take a few minutes to be indexed.
Inputs: query (required), page. Returns: object
filter_requestersFilter requestersARead-onlyIdempotent
Search requesters (30 per page, newest first; results cannot be sorted).
Fields: first_name, last_name, name, job_title, primary_email, work_phone_number, mobile_phone_number, department_id, reporting_manager_id, time_zone, language, location_id, created_at, updated_at, external_id and custom fields. The ~ operator does "starts with" on first_name, last_name, name, primary_email, mobile_phone_number and work_phone_number. Changes can take a few minutes to be indexed.
Inputs: query (required), page, include_agents. Returns: object
filter_ticketsFilter ticketsARead-onlyIdempotent
Search tickets with the Freshservice query language (30 results per page).
Filterable fields: requester_id, email, agent_id, group_id, priority, status, impact, urgency, tag, due_by and fr_due_by (YYYY-MM-DD), created_at and updated_at (YYYY-MM-DD or UTC datetime), workspace_id (0 = all workspaces) and custom fields by API name (see get_ticket_fields). Field names are case-sensitive snake_case. Examples: "status:2 AND priority:4", "agent_id:null AND created_at:>'2025-01-01'", "(group_id:12 OR group_id:15) AND status:3", "tag:'vip'". :> and :< are documented as inclusive (>= and <=). Values must be valid ones (status 2-5, priority 1-4, ...) or the API answers 400. The response includes "total" with the overall match count.
Inputs: query (required), page, workspace_id. Returns: object
get_agentGet agentBRead-onlyIdempotent
Get an agent by ID, including roles and group memberships.
Inputs: agent_id (required). Returns: object
get_agent_fieldsList agent fieldsBRead-onlyIdempotent
List agent fields, including custom fields.
Inputs: no named parameters. Returns: object
get_agent_groupGet agent groupARead-onlyIdempotent
Get an agent group by ID, including its members.
Inputs: group_id (required). Returns: object
get_all_agent_groupsList agent groupsCRead-onlyIdempotent
List agent groups.
Inputs: page, per_page. Returns: object
get_all_agentsList agentsCRead-onlyIdempotent
List agents.
Inputs: page, email, state, active, +1 more. Returns: object
get_all_requester_groupsList requester groupsCRead-onlyIdempotent
List requester groups.
Inputs: page, per_page. Returns: object
get_all_requestersList requestersBRead-onlyIdempotent
List requesters, optionally looked up by email or phone.
Inputs: page, email, active, per_page, +3 more. Returns: object
get_all_solution_categoryList solution categoriesCRead-onlyIdempotent
List knowledge-base categories.
Inputs: page, per_page, workspace_id. Returns: object
get_list_of_solution_articleList solution articlesARead-onlyIdempotent
List the articles in a folder: metadata plus a short plain-text snippet, not the full body.
Use get_solution_article with an article id to read the whole article.
Inputs: folder_id (required), page, filter, per_page, +1 more. Returns: object
get_list_of_solution_folderList solution foldersCRead-onlyIdempotent
List the folders in a category.
Inputs: category_id (required), page, filter, per_page. Returns: object
get_requested_itemsGet requested itemsARead-onlyIdempotent
List the service catalog items requested on a service-request ticket.
Inputs: ticket_id (required). Returns: object
get_requesterGet requesterCRead-onlyIdempotent
Get a requester by ID.
Inputs: requester_id (required). Returns: object
get_requester_groupGet requester groupBRead-onlyIdempotent
Get a requester group by ID.
Inputs: requester_group_id (required). Returns: object
get_service_itemGet service itemARead-onlyIdempotent
Get a service item, including its form fields (custom_fields names to use when placing a request).
Inputs: display_id (required). Returns: object
get_solution_articleGet solution articleARead-onlyIdempotent
Read a knowledge-base article by ID: the full HTML in description and plain text in description_text.
Use the id from search_solution_articles or get_list_of_solution_article.
Inputs: article_id (required). Returns: object
get_solution_categoryGet solution categoryBRead-onlyIdempotent
Get a knowledge-base category by ID.
Inputs: category_id (required). Returns: object
get_solution_folderGet solution folderCRead-onlyIdempotent
Get a knowledge-base folder by ID.
Inputs: folder_id (required). Returns: object
get_ticket_by_idGet ticketARead-onlyIdempotent
Get a ticket by ID. Returns {"ticket": {...}} with all standard fields, custom_fields, planned dates, SLA flags (is_escalated, fr_escalated), attachments and, when available, the resolution notes.
Inputs: ticket_id (required), include. Returns: object
get_ticket_fieldsGet ticket form fieldsBRead-onlyIdempotent
List ticket form fields, including custom fields, their API names and allowed choices.
Inputs: workspace_id. Returns: object
get_ticketsList ticketsARead-onlyIdempotent
List tickets (newest first). Without a filter or updated_since, only the last 30 days are returned.
For arbitrary conditions (status, priority, agent, group, dates, custom fields) use filter_tickets.
Inputs: page, type, email, filter, +6 more. Returns: object
list_all_requester_fieldsList requester fieldsARead-onlyIdempotent
List requester fields, including custom fields and their API names.
Inputs: no named parameters. Returns: object
list_all_ticket_conversationList ticket conversationsARead-onlyIdempotent
List the replies and notes on a ticket, oldest first. Each has body/body_text, private, incoming, source, user_id, to/cc/bcc emails and attachments.
Inputs: ticket_id (required), page, per_page. Returns: object
list_requester_group_membersList requester group membersCRead-onlyIdempotent
List the requesters in a requester group.
Inputs: requester_group_id (required), page, per_page. Returns: object
list_rolesList agent rolesARead-onlyIdempotent
List agent roles (needed for create_agent's roles).
Inputs: page, per_page. Returns: object
list_service_catalog_categoriesList service catalog categoriesCRead-onlyIdempotent
List service catalog categories.
Inputs: workspace_id. Returns: object
list_service_itemsList service itemsBRead-onlyIdempotent
List one page of service catalog items.
Inputs: page, per_page, category_id, workspace_id. Returns: object
list_solution_subfoldersList solution sub-foldersCRead-onlyIdempotent
List the sub-folders of a folder.
Inputs: folder_id (required), page, per_page. Returns: object
list_ticket_activitiesList ticket activitiesARead-onlyIdempotent
List the audit trail of a ticket (field changes, assignments, status changes).
Inputs: ticket_id (required). Returns: object
list_ticket_sourcesList ticket sourcesARead-onlyIdempotent
List ticket sources (channels), built-in and custom, with the numeric IDs create_ticket expects.
Returns {"sources": [{"id": 1, "value": "Email"}, ...]}. Freshservice has no endpoint that lists sources (/ticket_fields/sources only creates one), so they are read from the ticket form's "source" field.
Inputs: workspace_id. Returns: object
list_ticket_time_entriesList ticket time entriesBRead-onlyIdempotent
List time entries (time tracked by agents) on a ticket.
Inputs: ticket_id (required), page, per_page. Returns: object
publish_solution_articlePublish solution articleBIdempotent
Publish a draft article (sets status to 2).
Inputs: article_id (required). Returns: object
reactivate_agentReactivate agentBIdempotent
Reactivate a deactivated agent.
Inputs: agent_id (required). Returns: object
reactivate_requesterReactivate requesterAIdempotent
Reactivate a deactivated requester.
Inputs: requester_id (required). Returns: object
remove_requester_from_groupRemove requester from groupBIdempotent
Remove a requester from a manual requester group.
Inputs: requester_id (required), requester_group_id (required). Returns: object
restore_ticketRestore ticketAIdempotent
Restore a deleted (trashed) ticket.
Inputs: ticket_id (required). Returns: object
search_service_itemsSearch service itemsCRead-onlyIdempotent
Search service catalog items by keyword.
Inputs: search_term (required), page, per_page, user_email, +1 more. Returns: object
search_solution_articlesSearch solution articlesARead-onlyIdempotent
Full-text search across published knowledge-base articles.
Each result has id, title, folder_id/category_id, status and a short plain-text snippet (the first
~300 characters). Freshservice returns every article's full body here, which makes 30 results very
large, so they are trimmed unless include_body is true. To read an article, call get_solution_article
with its id (full HTML in description, plain text in description_text).
Inputs: search_term (required), page, per_page, user_email, +1 more. Returns: object
send_ticket_replyReply to ticketB
Send a public reply to the requester on a ticket.
Inputs: body (required), ticket_id (required), user_id, cc_emails, +3 more. Returns: object
update_agentUpdate agentBIdempotent
Update an agent. Only the supplied fields change.
Inputs: agent_id (required), agent_fields (required). Returns: object
update_groupUpdate agent groupAIdempotent
Update an agent group. Only the supplied fields change.
Inputs: group_id (required), group_fields (required). Returns: object
update_requesterUpdate requesterBIdempotent
Update a requester. Only the supplied fields change.
Inputs: requester_id (required), requester_fields (required). Returns: object
update_requester_groupUpdate requester groupCIdempotent
Update a requester group.
Inputs: requester_group_id (required), name, conditions, description. Returns: object
update_solution_articleUpdate solution articleCIdempotent
Update a knowledge-base article.
Inputs: article_id (required), tags, title, status, +5 more. Returns: object
update_solution_categoryUpdate solution categoryCIdempotent
Update a knowledge-base category.
Inputs: category_id (required), name, description, default_category. Returns: object
update_solution_folderUpdate solution folderCIdempotent
Update a knowledge-base folder.
Inputs: folder_id (required), name, group_ids, visibility, +3 more. Returns: object
update_ticketUpdate ticketBIdempotent
Update a ticket. Only the supplied fields change.
Inputs: ticket_id (required), ticket_fields (required), attachments. Returns: object
update_ticket_conversationUpdate ticket conversationAIdempotent
Edit a note or reply. Only notes and replies created through the API/agents can be edited.
Inputs: body (required), conversation_id (required), attachments. Returns: object
update_ticket_time_entryUpdate ticket time entryCIdempotent
Update a time entry on a ticket. Time entry fields: time_spent ('hh:mm'), note, agent_id, billable, executed_at (ISO-8601), timer_running, task_id, custom_fields.
Inputs: ticket_id (required), time_entry_id (required), time_entry_fields (required). Returns: object
view_ticket_time_entryView ticket time entryBRead-onlyIdempotent
Get one time entry on a ticket.
Inputs: ticket_id (required), time_entry_id (required). Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 76 tools
Most tools are separated by resource and action (agent vs requester vs ticket vs solution), and descriptions clarify list/search/get variants. A few boundaries are fuzzy—e.g. create_group is an agent group while requester groups are explicitly prefixed, and solution folder/article listing uses several get/list patterns—but tools are not broadly indistinguishable.
Names are all snake_case and generally verb_noun, but the verb vocabulary is inconsistent: get_all_agents vs list_roles vs filter_agents, get_list_of_solution_folder vs list_solution_subfolders, and singular get_all_solution_category. The pattern is readable but not predictable enough for a top score.
76 tools is far beyond the 3–15 well-scoped range and exceeds the 50+ extreme-mismatch threshold. Even for a broad ITSM product, this surface should be split or namespaced into smaller MCP servers.
Core service-desk resources have strong lifecycle coverage: tickets (create/update/delete/restore, conversations, time entries), agents/requesters (create/update/deactivate/reactivate), groups (CRUD), and knowledge base (CRUD/search/publish). Gaps remain around admin CRUD for service catalog items and broader ITSM modules such as changes, problems, and assets, but the apparent service-desk scope is largely covered.
Maintenance
Related MCP Connectors
Freshdesk MCP Pack — helpdesk ticket and contact management via Freshdesk API v2.
MCP server for GLPI: tickets, ITIL, assets, knowledge base. GLPI 10/11. Not affiliated with Teclib'.
MCP server for Support & Service Management
Related MCP Servers
- AlicenseBqualityDmaintenanceMCP server created for Freshservice, allowing AI models to interact with Freshservice modules5939MIT
- FlicenseNot gradedqualityNot gradedmaintenanceA comprehensive MCP server that provides 159 tools for interacting with the Freshservice ITSM platform, covering modules such as tickets, assets, and project management. It enables users to manage service desk operations and configuration through the Model Context Protocol.-
- AlicenseAqualityDmaintenanceProvides a standard MCP interface for Freshdesk helpdesk operations, including ticket lifecycle management, draft/send replies and notes, contact lookup, and agent/group discovery.14MIT
- AlicenseNot gradedqualityDmaintenanceMCP server to interact with ServiceNow instances, enabling ITSM, CMDB, workflow, and knowledge search operations.MIT