Conversation
Adds four tools aimed at "ticket archaeology" workflows where an agent needs the entire ticket history without manual pagination: - get_ticket_full: ticket + ALL conversations (paginated, no truncation) plus optional requester/agent expansion and status label decoding. Returns an attachments_index covering both ticket-level and per-conversation attachments. - download_ticket_attachments: parallel download of every attachment to disk under <dest_dir>/<ticket_id>/, with a per-file size cap. - extract_inline_images: resolves cid: refs against attachments and downloads remote <img src> URLs from the description and every conversation body. Handles data: URIs too. - decode_ticket_status: int -> label using ticket_fields metadata (covers custom statuses). Existing get_ticket / get_ticket_conversation behavior is unchanged. New tools share helpers (_fd_get, _fetch_all_conversations, _load_status_map) and the existing parse_link_header for pagination. Also drops `build>=1.2.2.post1` from runtime dependencies — it's a build-time tool, not a runtime requirement, and ships ~1MB of extra dependencies into every install. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mpasternak
added a commit
to mpasternak/freshdesk_mcp
that referenced
this pull request
Aug 16, 2026
Ticket bodies come from whoever emailed the helpdesk, so anything derived from them is attacker-influenced input. The bulk fetch tools merged from upstream PR effytech#47 treated it as trusted. - extract_inline_images fetched any http(s) URL found in an <img src>. A customer could mail <img src="http://169.254.169.254/latest/meta-data/"> and have the server request it, write the response to disk and hand the path to the model. URLs harvested from ticket HTML are now checked against loopback, link-local, private, reserved, multicast and unspecified ranges, and non-http(s) schemes are refused. Redirects are followed one hop at a time and re-checked, because automatic redirect following would let a public URL bounce to 127.0.0.1. Attachment URLs issued by Freshdesk are exempt - they are not attacker-controlled. - Conversation paging looped until Freshdesk stopped advertising a next page. It now stops at FRESHDESK_MAX_CONVERSATION_PAGES (default 50) and reports conversations_truncated instead of silently implying it read everything. - Attachment downloads were fired all at once through asyncio.gather. They now run behind a semaphore (FRESHDESK_DOWNLOAD_CONCURRENCY, default 5) and stop at FRESHDESK_MAX_DOWNLOAD_FILES (default 200), reporting skipped_over_limit. - Downloads defaulted to /tmp/fd, a shared path any local account can read or pre-create. The default is now a per-user directory created mode 0700. Tests cover the URL checks, the refusal to issue a request for a blocked address, and the paging cap. Both workflows run them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ytwHf2AcXX4ZzN4Zd6c6i
|
@RajRoR I merged this branch into a fork I run against a live Freshdesk — the bulk fetch tools are useful,
9 tests included, verified against this branch as-is on MCP SDK 1.4.1. The patch is SDK-independent, so it doesn't collide with #50. Merge it, cherry-pick it, or rewrite it your way — no ownership implied, it's your PR. Findings came from an automated security review and were then verified by hand. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds four tools aimed at "ticket archaeology" workflows where an agent needs the entire ticket history without manual pagination, plus a small
pyproject.tomlcleanup.New tools
get_ticket_full— ticket + ALL conversations (paginated, no truncation), plus optional requester/agent expansion and status-label decoding. Returns anattachments_indexcovering both ticket-level and per-conversation attachments.download_ticket_attachments— parallel download of every attachment to disk under<dest_dir>/<ticket_id>/, with a per-file size cap (default 50 MB).extract_inline_images— resolvescid:refs against attachments and downloads remote<img src>URLs from the description and every conversation body. Handlesdata:URIs too.decode_ticket_status— int → label usingticket_fieldsmetadata (covers custom statuses).These have proven valuable in agent workflows where the model needs to reason over an entire ticket including embedded screenshots, without the user manually paginating conversations or matching
cid:refs to attachment URLs by hand.Cleanup
build>=1.2.2.post1from runtimedependencies— it's a build-time tool, not a runtime requirement, and ships ~1MB of extra dependencies into every install.Behavior unchanged
get_ticket/get_ticket_conversationare untouched.parse_link_headerfor pagination.Implementation notes
_fd_get,_fetch_all_conversations,_load_status_map) are private and named with underscore prefix to keep them out of the tool surface._STATUS_CACHE.httpxand enforce a size limit before writing.html.parser(no new dep).Caveat for hosts
get_ticket_fulloutput for busy tickets routinely exceeds 256 KB / 25 k tokens. README notes this and recommends slicing the persisted result withjq.Test plan (verified against live Freshdesk)
uv buildproduces sdist + wheel cleanlyfreshdesk-mcpboots viauvx --from . freshdesk-mcp(env-only, exit 0)get_ticket_fullreturns 25-conversation ticket withstatus_label='Action Plan Provided',attachments_indexpopulateddownload_ticket_attachmentsruns end-to-end (count_saved/count_errors reported; ticket under test had 0 file attachments, only inline)extract_inline_imagesresolves 6 inline<img>URLs to disk; non-zero file sizes verifiedextract_inline_images(size_limit_mb=0)produces 6file exceeds size_limiterror entries and 0 saved filesdecode_ticket_status(12)→Action Plan Provided;decode_ticket_status(99999)→ fallbackCustom(99999)🤖 Generated with Claude Code