freshservice-mcp 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -207,6 +207,8 @@ Freshservice Changes use numeric values for their standard fields:
207
207
 
208
208
  `associate_tickets_to_change` uses the association shape documented by Freshservice's Ticket API. The default relationship, `change_initiated_by_ticket`, means the ticket initiated the Change. Use `association_type: "change_initiating_ticket"` when the Change initiated the ticket. `change_id` is placed in the documented nested `display_id` field.
209
209
 
210
+ Because the association is applied as a ticket update, Freshservice validates the whole ticket — so on an instance where `description` is a mandatory field, a ticket with an empty description would otherwise be rejected with a 400. The tool handles this transparently: it retries once with a minimal placeholder description, and only for tickets that genuinely have none. Existing descriptions are never overwritten.
211
+
210
212
  ## Local Development
211
213
 
212
214
  ```bash
package/dist/index.js CHANGED
@@ -11180,12 +11180,29 @@ async function updateChange(config2, changeId, payload) {
11180
11180
  async function associateTicketsToChange(config2, changeDisplayId, ticketIds, associationType = "change_initiated_by_ticket") {
11181
11181
  return Promise.all(
11182
11182
  ticketIds.map(
11183
- (ticketId) => updateTicket(config2, ticketId, {
11184
- [associationType]: { display_id: changeDisplayId }
11185
- })
11183
+ (ticketId) => associateOneTicket(config2, ticketId, changeDisplayId, associationType)
11186
11184
  )
11187
11185
  );
11188
11186
  }
11187
+ function isBlankDescriptionError(error2) {
11188
+ const message = error2 instanceof Error ? error2.message : String(error2);
11189
+ return message.includes("400") && message.includes('"field":"description"') && message.toLowerCase().includes("blank");
11190
+ }
11191
+ async function associateOneTicket(config2, ticketId, changeDisplayId, associationType) {
11192
+ const association = { [associationType]: { display_id: changeDisplayId } };
11193
+ try {
11194
+ return await updateTicket(config2, ticketId, association);
11195
+ } catch (error2) {
11196
+ if (!isBlankDescriptionError(error2)) throw error2;
11197
+ const existing = await getTicket(config2, ticketId);
11198
+ const currentDescription = existing.description_text?.trim() || stripHtml(existing.description ?? "");
11199
+ if (currentDescription) throw error2;
11200
+ return updateTicket(config2, ticketId, {
11201
+ ...association,
11202
+ description: BLANK_DESCRIPTION_PLACEHOLDER
11203
+ });
11204
+ }
11205
+ }
11189
11206
  async function addChangeNote(config2, changeId, payload) {
11190
11207
  const data = await fsPost(
11191
11208
  config2,
@@ -11206,10 +11223,11 @@ async function listAgents(config2, options = {}) {
11206
11223
  const data = await fsGet(config2, "agents", params);
11207
11224
  return data.agents ?? [];
11208
11225
  }
11209
- var DEFAULT_STATUS_MAP, PRIORITY_MAP, CHANGE_PRIORITY_MAP, CHANGE_STATUS_MAP, CHANGE_TYPE_MAP, CHANGE_IMPACT_MAP, CHANGE_RISK_MAP;
11226
+ var BLANK_DESCRIPTION_PLACEHOLDER, DEFAULT_STATUS_MAP, PRIORITY_MAP, CHANGE_PRIORITY_MAP, CHANGE_STATUS_MAP, CHANGE_TYPE_MAP, CHANGE_IMPACT_MAP, CHANGE_RISK_MAP;
11210
11227
  var init_freshservice_client = __esm({
11211
11228
  "src/freshservice-client.ts"() {
11212
11229
  "use strict";
11230
+ BLANK_DESCRIPTION_PLACEHOLDER = ".";
11213
11231
  DEFAULT_STATUS_MAP = {
11214
11232
  2: "Open",
11215
11233
  3: "Pending",
@@ -21857,7 +21875,7 @@ ${JSON.stringify(normalizeChange(change), null, 2)}`
21857
21875
  "associate_tickets_to_change",
21858
21876
  {
21859
21877
  title: "Associate Tickets to Change",
21860
- description: "Associate existing tickets with a Change by updating each ticket using Freshservice's documented Change association object.",
21878
+ description: "Associate existing tickets with a Change by updating each ticket using Freshservice's documented Change association object. A ticket whose description is empty on an instance where description is mandatory is retried once with a minimal placeholder description so the association still lands; existing descriptions are never overwritten.",
21861
21879
  inputSchema: external_exports.object({
21862
21880
  change_id: external_exports.number().int().positive().describe(
21863
21881
  "Change display ID (Freshservice requires display_id in the association body)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freshservice-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "MCP server for Freshservice – manage tickets, changes, agents and conversations from any MCP-compatible AI client",
5
5
  "keywords": [
6
6
  "mcp",