A configurable MCP server for Freshservice with permission modes
Project description
Freshservice MCP Server
A configurable MCP server for the Freshservice REST API with permission-based tool gating.
Freshservice only provides a single API key per account with no scoping. This server addresses that by introducing a FRESHSERVICE_MODE environment variable that controls which tools are exposed. Blocked tools are excluded at registration time, so the LLM never sees them.
Permission Modes
| Mode | Tools | Description |
|---|---|---|
read |
54 | List, view, filter, search across all modules. File search. No modifications. |
standard |
81 | Read + create/update tickets, changes, approvals, service requests, attachments, time entries. No deletes, no agent/group admin. |
full |
97 | Standard + products, requesters, solution articles, delete notes/tasks/time entries. No agent creation, no ticket/change deletion. |
admin |
107 | Everything. Create/delete agents, groups, tickets, changes, attachments. |
Default mode is read.
Setup
Prerequisites
- uv (recommended) — manages Python and dependencies for you, with hash-verified installs
- or Python 3.11+ with pip
- A Freshservice account with API access enabled
Getting Your Freshservice API Key
- Log into your Freshservice portal as an agent
- Click your profile picture in the top-right corner
- Go to Profile Settings
- Your API key is shown in the right sidebar under Your API Key
- Copy it — you'll need it for configuration below
Note: Freshservice provides a single API key per agent with no scoping. This is why the server has permission modes — so you can limit what the LLM can do, even though the API key itself has full access.
Environment Variables
| Variable | Required | Description |
|---|---|---|
FRESHSERVICE_DOMAIN |
Yes | Your Freshservice domain (e.g. yourcompany.freshservice.com) |
FRESHSERVICE_APIKEY |
Yes | Your Freshservice API key |
FRESHSERVICE_MODE |
No | Permission mode: read, standard, full, or admin. Default: read |
FRESHSERVICE_CACHE_TTL |
No | Cache lifetime in seconds for schema/reference data. Default: 3600 (1 hour). Set to 0 to disable caching. |
FRESHSERVICE_FILE_SEARCH_PATHS |
No | Semicolon-separated list of directories the find_file tool can search (e.g. C:\Users\me\Docs;C:\Exports). Required for file discovery. |
FRESHSERVICE_AUDIT_LOG |
No | Path to a JSON-lines audit log (e.g. C:\logs\freshservice-audit.jsonl). Every tool call is logged with sanitized arguments, success/error state, and timing. Disabled when unset. |
Install
Option A — uv (recommended). No install step at all: uvx fetches the package from PyPI on first run and caches it. Pin the version so upgrades are deliberate:
uvx node804-freshservice-mcp==1.2.0
Option B — pip:
pip install node804-freshservice-mcp
Option C — from source (development):
git clone <this-repo>
cd node804-freshservice-mcp
uv sync # or: pip install -e ".[dev]"
Each option provides the node804-freshservice-mcp command and the node804_freshservice_mcp Python module.
Claude Desktop
-
Open your Claude Desktop config file:
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - Mac:
~/Library/Application Support/Claude/claude_desktop_config.json
- Windows:
-
Add the
node804-freshservice-mcpentry undermcpServers(create the file if it doesn't exist):
{
"mcpServers": {
"FreshService": {
"command": "uvx",
"args": ["node804-freshservice-mcp==1.2.0"],
"env": {
"FRESHSERVICE_APIKEY": "your_api_key",
"FRESHSERVICE_DOMAIN": "yourcompany.freshservice.com",
"FRESHSERVICE_MODE": "standard",
"FRESHSERVICE_CACHE_TTL": 3600
}
}
}
}
If you installed with pip instead of uv, use "command": "python" with "args": ["-m", "node804_freshservice_mcp.server"].
- Restart Claude Desktop — the server starts automatically when Claude needs it.
Using a .env file instead of inline env values:
If you'd rather keep credentials out of claude_desktop_config.json, create a .env file in the repo root (see .env.example) and use cwd to point the server at it:
cp .env.example .env # edit with your credentials
{
"mcpServers": {
"node804-freshservice-mcp": {
"command": "uvx",
"args": ["node804-freshservice-mcp==1.2.0"],
"cwd": "/path/to/node804-freshservice-mcp"
}
}
}
The server calls load_dotenv() on startup, which reads .env from the working directory. The cwd field tells Claude Desktop to start the server in the repo root where that file lives.
Claude Code (CLI)
claude mcp add node804-freshservice-mcp \
-e FRESHSERVICE_APIKEY=your_api_key \
-e FRESHSERVICE_DOMAIN=yourcompany.freshservice.com \
-e FRESHSERVICE_MODE=standard \
-- uvx node804-freshservice-mcp==1.2.0
To verify it was added:
claude mcp list
Verifying It Works
Once connected, ask your LLM:
- "What's my access level?" → calls
server_status, shows the mode, tool count, and rate-limit info - "Who am I in Freshservice?" → calls
get_current_agent, confirms the API key is valid and returns your agent profile
If either fails, check the Troubleshooting section below.
Project Structure
src/node804_freshservice_mcp/
__init__.py # Package marker + version
server.py # FastMCP instance, conditional_tool decorator, main()
config.py # Permission modes, tool hierarchy, mode summary
client.py # Shared httpx client, rate-limit transport, response cache, auth
models.py # Enums (TicketSource, ChangeStatus, ...) + Pydantic models
tools/
__init__.py # Imports all tool modules to trigger registration
tickets.py # 24 tools - tickets, conversations, attachments, time entries
changes.py # 33 tools - changes, approvals, notes, tasks, time entries
service_catalog.py # 3 tools - service items + requests
products.py # 4 tools - product CRUD
requesters.py # 6 tools - requester CRUD + fields
agents.py # 7 tools - current agent, agent CRUD + fields
groups.py # 10 tools - agent groups + requester groups
canned_responses.py # 4 tools - canned responses + folders
workspaces.py # 2 tools - workspace listing
solutions.py # 13 tools - KB categories, folders, articles
files.py # 1 tool - local file search for attachment discovery
Mode Details
read (default)
Safe for anyone who needs to look up ticket info, check change status, or search the knowledge base. Cannot modify anything.
Includes: get/list/filter/view operations for tickets, changes, agents, requesters, groups, products, service catalog, canned responses, workspaces, and solution articles.
standard
Day-to-day IT work. Create and update tickets, manage changes through their lifecycle, send replies, add notes, handle approvals. Cannot delete anything or manage agents/groups.
Includes everything in read plus: create/update tickets, create/update changes, close changes, ticket replies and notes, attachments, change tasks/notes/time entries, approval management, service requests.
full
Adds content management and requester administration. For senior staff who need to manage the knowledge base and requester records.
Includes everything in standard plus: create/update products, create/update requesters, solution article/folder/category management, delete change notes/tasks/time entries, requester group membership.
admin
Full access to everything, including destructive operations. Only use this if you specifically need to create agents, delete tickets or changes, or manage group structures.
Includes everything in full plus: delete tickets, delete changes, create/update agents, create/update agent groups, create/update requester groups.
Attachments
The server provides attachment tools for uploading, listing, and deleting files on tickets.
Finding files
Use find_file to search for files on the local filesystem by partial name or glob pattern before attaching them. This is the recommended first step when a user asks to find, locate, or attach files from their system — for example, asking to "attach the Q4 report" will find Q4_Report.pdf in any configured search directory.
Searches are restricted to directories listed in the FRESHSERVICE_FILE_SEARCH_PATHS environment variable (semicolon-separated). Matching is case-insensitive and supports partial names (report), glob patterns (*.pdf), and recursive subdirectory search.
Uploading
Use add_ticket_attachment, add_note_attachment, and add_reply_attachment to upload files. These tools take an absolute file path on the MCP server's local filesystem, read the file, detect MIME type, and upload it via multipart form-data.
Note: The MCP server must have filesystem access to the file being attached. These tools are designed for scenarios where the MCP client (e.g. Claude Code) runs on the same machine or can write files to a path the server can read.
Listing and deleting
list_ticket_attachments— list all attachments on a ticket (read mode)delete_ticket_attachment/delete_conversation_attachment— remove attachments (admin mode)
Freshservice limit: The total size of all attachments on a single ticket cannot exceed 40 MB.
Agent Signatures
The send_ticket_reply and add_reply_attachment tools automatically append the current agent's HTML signature to every outbound reply. The signature is pulled from your Freshservice agent profile (the same one configured under Profile Settings → Signature in the Freshservice portal).
- Signature data comes from the cached
get_current_agentcall, so there is zero additional API cost. - If no signature is configured in your profile, replies are sent without one.
- Internal notes (
create_ticket_note,add_note_attachment) are not affected — signatures are only appended to replies visible to the requester.
Prompt Examples
Once connected, you can ask things like:
| Prompt | What happens | API calls |
|---|---|---|
| "Show me my open tickets" | get_my_tickets — looks up your agent ID, discovers all non-resolved statuses, and filters in one shot |
1 (warm cache) |
| "What's ticket #4521 about?" | get_ticket_by_id |
1 |
| "Create a ticket for the printer on floor 3" | create_ticket with subject/description/priority |
1 |
| "Add a note to ticket #4521 saying we ordered parts" | create_ticket_note |
1 |
| "Reply to ticket #4521 letting them know it's fixed" | send_ticket_reply — auto-appends your agent signature |
1 |
| "Find that Q4 report" | find_file — searches configured directories for matching files |
0 |
| "Attach this screenshot to ticket #4521" | add_ticket_attachment with the file path |
1 |
| "Show all changes awaiting approval" | filter_changes with query status:3 |
1 |
| "What ticket statuses do we have?" | get_ticket_statuses — returns every status categorized as resolved/unresolved |
0 (cached) |
| "Who am I in Freshservice?" | get_current_agent — returns your agent profile |
0 (cached) |
| "What's my access level?" | server_status — shows mode, tool counts, rate limit |
0 |
| "The admin added new statuses, refresh the cache" | clear_cache — drops all cached data |
0 |
API Efficiency
Freshservice enforces a per-hour tenant-wide API rate limit. This server is designed to stay well within it:
- 1 tool call = 1 API call for most tools. The server makes zero background calls, zero polling, and zero prefetching — every API call is triggered only when the LLM explicitly invokes a tool. Attachment uploads use multipart form-data and count as a single API call each.
- Compound convenience tools:
get_my_ticketschains agent lookup + status discovery + filter into a single tool call (1 API call on warm cache, up to 3 on first use).get_ticket_statusesparses cached field data at zero API cost. - Rate-limit handling: A custom transport reads
X-RateLimit-Remainingon every response and automatically retries on HTTP 429 with theRetry-Afterdelay (up to 3 retries). - Response caching: Schema and reference-data tools cache successful responses for the duration set by
FRESHSERVICE_CACHE_TTL(default 1 hour). These are admin-configured structures that effectively never change mid-session. Repeated lookups cost zero API calls. Use theclear_cachetool to force a refresh. - Pagination controls: All list endpoints expose
pageandper_pageparameters so the LLM fetches only what it needs. Auto-paginating tools (list_service_items,filter_agents) default to 3 pages max.
Always-On Tools
The server_status and clear_cache tools are always available regardless of mode.
server_status— Reports the current mode, tool counts, capabilities, cache TTL, and API rate-limit remaining.clear_cache— Drops all cached responses so the next call fetches fresh data from the API.
Security Notes
- API key is only sent to your Freshservice domain via Basic auth over HTTPS
- No telemetry, no external calls, no logging of credentials
- Credentials are validated at startup; the server fails fast if
FRESHSERVICE_DOMAINorFRESHSERVICE_APIKEYare missing - Mode restriction is enforced at tool registration time. Blocked tools don't exist in the MCP server's tool list, so the LLM cannot call them.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
RuntimeError: FRESHSERVICE_DOMAIN environment variable is required |
Missing or empty FRESHSERVICE_DOMAIN |
Make sure the env block in your MCP config has the correct variable name and value |
RuntimeError: FRESHSERVICE_APIKEY environment variable is required |
Missing or empty FRESHSERVICE_APIKEY |
Same — check the env block, or your .env file if running from source |
Server starts but tools return 401 Unauthorized |
Invalid API key | Regenerate your key in Freshservice Profile Settings and update your config |
Server starts but tools return 404 Not Found |
Wrong domain format | Use the full domain (yourcompany.freshservice.com), not just the subdomain name |
| Tools you expect are missing | Mode is too restrictive | Check server_status to see the current mode, then set FRESHSERVICE_MODE to a higher level |
No module named node804_freshservice_mcp |
Package not installed | Use the uvx config (installs automatically), or run pip install node804-freshservice-mcp |
| Stale data after admin changes | Cached response | Ask the LLM to call clear_cache, or set FRESHSERVICE_CACHE_TTL=0 to disable caching |
Development
uv sync # creates .venv from pyproject.toml + uv.lock
uv run pytest tests/
Or with pip: pip install -e ".[dev]" then pytest tests/.
uv.lock pins the full dependency graph with hashes for reproducible installs. After changing dependencies in pyproject.toml, run uv lock and commit the updated lockfile.
Heritage & What Changed
Originally derived from effytech/freshservice_mcp, which provided a single-file proof of concept wiring Freshservice endpoints to MCP. Roughly 15-20% of the original code survives — mainly the basic API call patterns and endpoint URLs.
Everything else has been rewritten or added from scratch:
| Area | Original | Now |
|---|---|---|
| Structure | Single file, flat | Modular package — 10 tool modules, shared client, config, models |
| Safety | All tools exposed, single unscoped key | 4 permission modes; blocked tools excluded at registration time |
| Rate limiting | None | Auto-retry on 429 with Retry-After, rate-limit tracking via server_status |
| Caching | None | Configurable TTL cache on schema/reference data, clear_cache tool |
| Pagination | ~14 tools returned page 1 only | All list tools expose page/per_page; auto-paginators capped at 3 pages |
| Convenience tools | None | get_my_tickets, get_ticket_statuses, get_current_agent |
| Tool descriptions | Generic one-liners | Enum values, query syntax examples, cross-references, behavioral context |
| Credential handling | None | Validated at startup, fail-fast on missing env vars |
| Tests | None | 55 tests covering cache, rate-limiting, config, permission-map consistency, convenience tools, and attachments |
| Tool count | ~50 | 107 permission-gated + 2 always-on |
AI-Assisted Development
Portions of this project including code, tests, and documentation were developed with the assistance of generative AI (Anthropic's Claude). All changes are human-reviewed before merge and exercised by the automated test suite, but as with any software, review the code and test against a non-production Freshservice instance before trusting it with write access (standard mode or above) to a live tenant.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file node804_freshservice_mcp-1.2.0.tar.gz.
File metadata
- Download URL: node804_freshservice_mcp-1.2.0.tar.gz
- Upload date:
- Size: 93.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04e47c3e3d0065c3a96cc27c4bd8954808cac9df7c7d3eca39587681d898181c
|
|
| MD5 |
e07cd894280221e997294952242353fe
|
|
| BLAKE2b-256 |
ac502e882b618218eb9ec68a5e8f86bf5de5af38b08d52bb547934f637efa9c3
|
Provenance
The following attestation bundles were made for node804_freshservice_mcp-1.2.0.tar.gz:
Publisher:
publish.yml on Node804/node804-freshservice-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
node804_freshservice_mcp-1.2.0.tar.gz -
Subject digest:
04e47c3e3d0065c3a96cc27c4bd8954808cac9df7c7d3eca39587681d898181c - Sigstore transparency entry: 1783616728
- Sigstore integration time:
-
Permalink:
Node804/node804-freshservice-mcp@4a2cc35256fbde988f351400bd6ed238c01d31ad -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Node804
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4a2cc35256fbde988f351400bd6ed238c01d31ad -
Trigger Event:
push
-
Statement type:
File details
Details for the file node804_freshservice_mcp-1.2.0-py3-none-any.whl.
File metadata
- Download URL: node804_freshservice_mcp-1.2.0-py3-none-any.whl
- Upload date:
- Size: 46.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
280dbc12956478b45ea087e0b6cc9cffb111158abf11d1872332ba83c3cf5fcb
|
|
| MD5 |
f9c5b64202f2d5bedf956ddc28de9d67
|
|
| BLAKE2b-256 |
9a03ed88591e8c3cbcdcddbfbe956c9f68d43c00827dcc5e6a70469de8797ae6
|
Provenance
The following attestation bundles were made for node804_freshservice_mcp-1.2.0-py3-none-any.whl:
Publisher:
publish.yml on Node804/node804-freshservice-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
node804_freshservice_mcp-1.2.0-py3-none-any.whl -
Subject digest:
280dbc12956478b45ea087e0b6cc9cffb111158abf11d1872332ba83c3cf5fcb - Sigstore transparency entry: 1783616980
- Sigstore integration time:
-
Permalink:
Node804/node804-freshservice-mcp@4a2cc35256fbde988f351400bd6ed238c01d31ad -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Node804
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4a2cc35256fbde988f351400bd6ed238c01d31ad -
Trigger Event:
push
-
Statement type: