# Freshservice MCP server (for Cowork / Claude Desktop) Cowork runs the agent inside an isolated VM, so a locally-installed *skill* can't see your credentials or reach your Freshservice host. This MCP server runs as a container on the LAN box (`192.168.101.12`) — outside the VM — and Claude Desktop connects to it through `mcp-remote`, exactly like the existing `sharepoint-lists` server. Credentials and network both live on the LAN box. (The skill in the parent directory remains the right tool for the Claude Code CLI, which is not sandboxed.) ## Deploy on 192.168.101.12 ```bash # on the LAN box, in this directory cp .env.example .env # edit .env: FRESHSERVICE_DOMAIN, FRESHSERVICE_API_KEY, MCP_AUTH_TOKEN # token: openssl rand -hex 32 docker compose up -d --build docker compose logs -f # confirm it started on :3838 inside the container ``` The container listens on container port 3838, published to host port **3839** (3838 is used by sharepoint-lists). Adjust in `docker-compose.yml` if needed. ### Verify ```bash # from the LAN box or any host that can reach it: curl -s -o /dev/null -w "%{http_code}\n" -X POST \ http://192.168.101.12:3839/mcp \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}' # expect 200 (401 means bad/missing token) ``` ## Connect Claude Desktop Add to `~/Library/Application Support/Claude/claude_desktop_config.json` under `mcpServers` (alongside `sharepoint-lists`): ```json "freshservice": { "command": "/opt/homebrew/bin/npx", "args": [ "-y", "mcp-remote", "http://192.168.101.12:3839/mcp", "--allow-http", "--header", "Authorization:${MCP_AUTH_HEADER}" ], "env": { "MCP_AUTH_HEADER": "Bearer " } } ``` Restart Claude Desktop. The Freshservice tools then appear in chat and Cowork. ## Multiple users The server can act as **each caller's own Freshservice identity** instead of a single shared one. Each client sends its own credentials as headers on every request; the server uses them per-call (they take precedence over the env vars). Actions are then attributed to each person, with their own permissions and rate limits. To run the server multi-user: 1. **Omit `FRESHSERVICE_DOMAIN` / `FRESHSERVICE_API_KEY` from `.env`** (keep only `MCP_AUTH_TOKEN`). With no env fallback, a client that forgets its headers gets a clean "Missing credentials" error rather than silently acting as whoever owns the env key. 2. Each user adds the extra headers to **their own** `claude_desktop_config.json`, with **their own** Freshservice API key (Profile settings → API key): ```json "freshservice": { "command": "/opt/homebrew/bin/npx", "args": [ "-y", "mcp-remote", "http://192.168.101.12:3839/mcp", "--allow-http", "--header", "Authorization:${MCP_AUTH_HEADER}", "--header", "X-Freshservice-Domain:${FS_DOMAIN}", "--header", "X-Freshservice-Key:${FS_KEY}" ], "env": { "MCP_AUTH_HEADER": "Bearer ", "FS_DOMAIN": "pesco.freshservice.com", "FS_KEY": "" } } ``` `MCP_AUTH_TOKEN` is the same shared value for everyone (it only gates *reaching* the server); `FS_KEY` is each person's own identity. The key stays on the user's machine and travels per-request — the server never stores it. > Note: the LAN transport is plain HTTP, so these headers travel unencrypted. > Fine on a trusted network; put a TLS-terminating reverse proxy in front if > this ever leaves it. ## Tools Tickets (`list_tickets`, `get_ticket`, `create_ticket`, `update_ticket`, `reply_ticket`, `add_note`, `list_ticket_conversations`), assets (`search_assets`, `get_asset`, `update_asset`), people (`find_requester`, `list_agents`, `list_groups`, `list_departments`), change/problem/release (`list_changes`, `get_change`, `create_change`, `list_problems`, `get_problem`, `list_releases`, `get_release`), and `freshservice_request` for any other v2 endpoint.