@fruggr/zendesk-mcp-server 3.5.3 → 3.6.0
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/dist/{http-DLa2DU80.js → http-DkjvhtQj.js} +2 -2
- package/dist/index.js +117 -76
- package/dist/sbom.cdx.json +4 -4
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { i as __toESM, n as __exportAll, r as __require, t as __commonJSMin } from "./rolldown-runtime-BbXq-jo1.js";
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as STARTUP_DOCS, C as WebStandardStreamableHTTPServerTransport, D as OAuthError, E as verifyBearerToken, M as errnoCode, O as OAuthErrorCode, S as DEFAULT_MAX_REQUEST_BODY_SIZE, T as isLegacyRequest, _ as zendeskGet, a as toolEnv, g as createAllTools, h as silentLogger, i as defaultServerFactory, j as createStartupError, k as compareCodeUnits, l as configDir, m as createRequestStateCodec, n as listenedUris, o as watchGrantAlerts, r as createServerCaches, s as startSlaRuntime, t as createListenFeed, v as REQUEST_TIMEOUT_MS, w as createMcpHandler, x as getOAuthUrls, y as deadlineSignal } from "./index.js";
|
|
4
4
|
import { a as supportedScopes, i as requestedScope, n as generateCodeVerifier, o as escapeHtml, t as generateCodeChallenge } from "./pkce-DE_v9NQy.js";
|
|
5
5
|
import * as util from "node:util";
|
|
6
6
|
import { format, inspect } from "node:util";
|
|
@@ -2816,7 +2816,7 @@ const EXPIRED = "This sign-in has expired or was opened in another browser. Star
|
|
|
2816
2816
|
const consentMemoryKey = (accountId, clientId, redirectUris) => createHash("sha256").update(JSON.stringify([
|
|
2817
2817
|
accountId,
|
|
2818
2818
|
clientId,
|
|
2819
|
-
[...redirectUris].sort()
|
|
2819
|
+
[...redirectUris].sort(compareCodeUnits)
|
|
2820
2820
|
])).digest("base64url");
|
|
2821
2821
|
const isExpiredLink = (err) => err instanceof Error && (err.name === "SessionNotFound" || err.name === "InvalidRequest");
|
|
2822
2822
|
const UID = /^[\w-]{1,64}$/;
|
package/dist/index.js
CHANGED
|
@@ -7630,6 +7630,14 @@ const isStartupError = (err) => err instanceof Error && err.name === "StartupErr
|
|
|
7630
7630
|
/** The errno code of a failed filesystem call, for the message. */
|
|
7631
7631
|
const errnoCode = (err) => err?.code ?? "unknown error";
|
|
7632
7632
|
//#endregion
|
|
7633
|
+
//#region src/utils/compare.ts
|
|
7634
|
+
/**
|
|
7635
|
+
* Orders strings by UTF-16 code unit, which is what a bare `.sort()` does.
|
|
7636
|
+
* Not `localeCompare`: an order that feeds a hash or a stored key must not move
|
|
7637
|
+
* with the runtime's locale data.
|
|
7638
|
+
*/
|
|
7639
|
+
const compareCodeUnits = (a, b) => Number(a > b) - Number(a < b);
|
|
7640
|
+
//#endregion
|
|
7633
7641
|
//#region src/utils/validation.ts
|
|
7634
7642
|
const formatPath = (path) => path.map(String).join(".");
|
|
7635
7643
|
const formatIssue$1 = (issue) => issue.path.length > 0 ? `${formatPath(issue.path)}: ${issue.message}` : issue.message;
|
|
@@ -7651,7 +7659,7 @@ const keysAt = (node, path) => {
|
|
|
7651
7659
|
const child = typeof head === "number" ? def.element : def.shape?.[String(head)];
|
|
7652
7660
|
return child && keysAt(child, rest);
|
|
7653
7661
|
};
|
|
7654
|
-
const keyList = (keys) => [...keys].sort().join(", ") || "(none)";
|
|
7662
|
+
const keyList = (keys) => [...keys].sort(compareCodeUnits).join(", ") || "(none)";
|
|
7655
7663
|
/** One sentence per object holding unknown keys, the top level first. */
|
|
7656
7664
|
const unknownKeysMessage = (schema, issues, topLevelKeys) => [...issues].sort((a, b) => a.path.length - b.path.length).map(({ path, keys }) => {
|
|
7657
7665
|
if (path.length === 0) return `Unknown parameter(s): ${keys.join(", ")}. Valid parameters: ${topLevelKeys}.`;
|
|
@@ -30832,10 +30840,11 @@ const fetchAllPages = async ({ subdomain, token, tool, path, extract, params = {
|
|
|
30832
30840
|
const items = [];
|
|
30833
30841
|
let page = 1;
|
|
30834
30842
|
while (true) {
|
|
30835
|
-
const
|
|
30843
|
+
const query = {
|
|
30836
30844
|
...params,
|
|
30837
30845
|
...buildOffsetParams(100, page)
|
|
30838
|
-
}
|
|
30846
|
+
};
|
|
30847
|
+
const response = await withForbiddenGuidance(tool, `GET ${path}`, () => zendeskGet(subdomain, token, path, query), forbiddenHint);
|
|
30839
30848
|
items.push(...extract(response) ?? []);
|
|
30840
30849
|
onPage?.(response);
|
|
30841
30850
|
if (!response.next_page) return items;
|
|
@@ -31273,70 +31282,6 @@ const createRequestTools = (env) => {
|
|
|
31273
31282
|
];
|
|
31274
31283
|
};
|
|
31275
31284
|
//#endregion
|
|
31276
|
-
//#region src/tools/search.ts
|
|
31277
|
-
const formatSearchResult = (result) => {
|
|
31278
|
-
const lines = [`## [${result["result_type"]}] #${result["id"]}`];
|
|
31279
|
-
if (result["subject"]) lines.push(`**Subject**: ${result["subject"]}`);
|
|
31280
|
-
if (result["name"]) lines.push(`**Name**: ${result["name"]}`);
|
|
31281
|
-
if (result["title"]) lines.push(`**Title**: ${result["title"]}`);
|
|
31282
|
-
if (result["email"]) lines.push(`**Email**: ${result["email"]}`);
|
|
31283
|
-
if (result["status"]) lines.push(`**Status**: ${result["status"]}`);
|
|
31284
|
-
if (result["description"]) lines.push(descriptionExcerpt(String(result["description"])));
|
|
31285
|
-
return lines.join("\n");
|
|
31286
|
-
};
|
|
31287
|
-
const createSearchTools = (env) => {
|
|
31288
|
-
const { subdomain } = env;
|
|
31289
|
-
return [{
|
|
31290
|
-
name: "search",
|
|
31291
|
-
namespace: "tickets",
|
|
31292
|
-
readOnly: true,
|
|
31293
|
-
title: "Zendesk Unified Search",
|
|
31294
|
-
description: "Search across tickets, users, and organizations. Supports filters like \"type:ticket status:open\", \"type:user role:agent\". Returns total count and paginated results (25 per page by default, up to 100); a ticket description is cut to a 200-character excerpt, which get_ticket returns in full. Organization results include name and ID only — use get_organization for full details (tags, domains, details).",
|
|
31295
|
-
inputSchema: object({
|
|
31296
|
-
query: string().min(1).describe("Zendesk search query. Supports type/status/role filters (e.g. \"type:ticket status:open\", \"type:user role:agent\") and free text; omit a type filter to search tickets, users and organizations at once."),
|
|
31297
|
-
per_page: number$1().int().min(1).max(100).default(25).describe(PER_PAGE_DESC),
|
|
31298
|
-
page: number$1().int().min(1).default(1).describe(PAGE_DESC)
|
|
31299
|
-
}),
|
|
31300
|
-
annotations: {
|
|
31301
|
-
readOnlyHint: true,
|
|
31302
|
-
destructiveHint: false,
|
|
31303
|
-
idempotentHint: true,
|
|
31304
|
-
openWorldHint: true
|
|
31305
|
-
},
|
|
31306
|
-
handler: async (params, { caller }) => {
|
|
31307
|
-
const { query, per_page, page } = params;
|
|
31308
|
-
const token = await caller.getToken();
|
|
31309
|
-
const response = await zendeskGet(subdomain, token, "/search", {
|
|
31310
|
-
query,
|
|
31311
|
-
...buildOffsetParams(per_page, page)
|
|
31312
|
-
});
|
|
31313
|
-
const results = response.results ?? [];
|
|
31314
|
-
const meta = extractSearchPaginationMeta(response, per_page, page);
|
|
31315
|
-
const header = (m) => `Total: ${m.count} | Page ${page} (${results.length} results)${m.has_more ? ` | Next page: ${m.next_page}` : ""}`;
|
|
31316
|
-
return { content: [{
|
|
31317
|
-
type: "text",
|
|
31318
|
-
text: formatFittingList(results, formatSearchResult, meta, offsetResume(page, per_page), header)
|
|
31319
|
-
}] };
|
|
31320
|
-
}
|
|
31321
|
-
}];
|
|
31322
|
-
};
|
|
31323
|
-
//#endregion
|
|
31324
|
-
//#region src/utils/elicitation.ts
|
|
31325
|
-
/**
|
|
31326
|
-
* Whether a client can answer a form elicitation. A bare `elicitation: {}`
|
|
31327
|
-
* predates the modes and counts as form support (spec rule the SDK follows).
|
|
31328
|
-
*/
|
|
31329
|
-
const supportsFormElicitation = (capabilities) => {
|
|
31330
|
-
const elicitation = capabilities?.elicitation;
|
|
31331
|
-
if (!elicitation) return false;
|
|
31332
|
-
return Object.keys(elicitation).length === 0 || "form" in elicitation;
|
|
31333
|
-
};
|
|
31334
|
-
const inputRequiredError = (result) => Object.assign(/* @__PURE__ */ new Error("The tool needs input from the user before it can answer."), {
|
|
31335
|
-
name: "InputRequiredError",
|
|
31336
|
-
inputRequired: result
|
|
31337
|
-
});
|
|
31338
|
-
const isInputRequiredError = (err) => err instanceof Error && err.name === "InputRequiredError" && "inputRequired" in err;
|
|
31339
|
-
//#endregion
|
|
31340
31285
|
//#region src/utils/sla-urgency.ts
|
|
31341
31286
|
/** Most urgent first: the order alerts are grouped and ranked in. */
|
|
31342
31287
|
const URGENCY_LEVELS = [
|
|
@@ -31543,6 +31488,95 @@ const fetchScope = async (scope, get, userId) => {
|
|
|
31543
31488
|
});
|
|
31544
31489
|
return mergeSnapshots(snapshots);
|
|
31545
31490
|
};
|
|
31491
|
+
//#endregion
|
|
31492
|
+
//#region src/utils/group-me.ts
|
|
31493
|
+
const GROUP_ME = /(^|\s)(-?)group:(?:me|"me")(?=\s|$)/gi;
|
|
31494
|
+
const NOT_A_VALUE = "group:me is not a Zendesk search value: \"me\" works only for user keywords (assignee:, requester:, submitter:), and Zendesk would silently match nothing.";
|
|
31495
|
+
/**
|
|
31496
|
+
* Reads `group:me` in a search query as the caller's group. With exactly one
|
|
31497
|
+
* group it becomes `group:<id>`, with a note to show; with none or several it
|
|
31498
|
+
* throws, naming the groups, since one offset-paginated search cannot cover
|
|
31499
|
+
* several groups. Without `group:me`, nothing is looked up.
|
|
31500
|
+
*/
|
|
31501
|
+
const resolveGroupMe = async (query, get, userId) => {
|
|
31502
|
+
if (query.match(GROUP_ME) === null) return { query };
|
|
31503
|
+
const groupIds = await readGroupIds(get, await userId());
|
|
31504
|
+
const [only, ...others] = groupIds;
|
|
31505
|
+
if (only === void 0) throw new Error(`${NOT_A_VALUE} You belong to no group, so there is no group queue to search: drop group:me, or name a group with group:<id>.`);
|
|
31506
|
+
if (others.length === 0) return {
|
|
31507
|
+
query: query.replace(GROUP_ME, (_, lead, negation) => `${lead}${negation}group:${only}`),
|
|
31508
|
+
note: `Note: group:me was read as group:${only}, your only group.`
|
|
31509
|
+
};
|
|
31510
|
+
const advice = [...query.matchAll(GROUP_ME)].every(([, , negation]) => negation === "-") ? `exclude a group with -group:<id> (e.g. -group:${only})` : `search one with group:<id> (e.g. group:${only}), one call per group`;
|
|
31511
|
+
throw new Error(`${NOT_A_VALUE} You belong to groups ${groupIds.join(", ")}: ${advice}.`);
|
|
31512
|
+
};
|
|
31513
|
+
/** `resolveGroupMe` for a tool call: the caller comes from the SLA service when wired, else `/users/me`. */
|
|
31514
|
+
const resolveCallerGroupMe = (query, subdomain, token, sla) => resolveGroupMe(query, (path, params) => zendeskGet(subdomain, token, path, params), sla?.userId ?? (() => zendeskGet(subdomain, token, "/users/me").then(({ user }) => user.id)));
|
|
31515
|
+
//#endregion
|
|
31516
|
+
//#region src/tools/search.ts
|
|
31517
|
+
const formatSearchResult = (result) => {
|
|
31518
|
+
const lines = [`## [${result["result_type"]}] #${result["id"]}`];
|
|
31519
|
+
if (result["subject"]) lines.push(`**Subject**: ${result["subject"]}`);
|
|
31520
|
+
if (result["name"]) lines.push(`**Name**: ${result["name"]}`);
|
|
31521
|
+
if (result["title"]) lines.push(`**Title**: ${result["title"]}`);
|
|
31522
|
+
if (result["email"]) lines.push(`**Email**: ${result["email"]}`);
|
|
31523
|
+
if (result["status"]) lines.push(`**Status**: ${result["status"]}`);
|
|
31524
|
+
if (result["description"]) lines.push(descriptionExcerpt(String(result["description"])));
|
|
31525
|
+
return lines.join("\n");
|
|
31526
|
+
};
|
|
31527
|
+
const createSearchTools = (env) => {
|
|
31528
|
+
const { subdomain } = env;
|
|
31529
|
+
return [{
|
|
31530
|
+
name: "search",
|
|
31531
|
+
namespace: "tickets",
|
|
31532
|
+
readOnly: true,
|
|
31533
|
+
title: "Zendesk Unified Search",
|
|
31534
|
+
description: "Search across tickets, users, and organizations. Supports filters like \"type:ticket status:open\", \"type:user role:agent\". Returns total count and paginated results (25 per page by default, up to 100); a ticket description is cut to a 200-character excerpt, which get_ticket returns in full. Organization results include name and ID only — use get_organization for full details (tags, domains, details).",
|
|
31535
|
+
inputSchema: object({
|
|
31536
|
+
query: string().min(1).describe("Zendesk search query. Supports type/status/role filters (e.g. \"type:ticket status:open\", \"type:user role:agent\") and free text; omit a type filter to search tickets, users and organizations at once. \"me\" is a value of the user keywords only (assignee:, requester:, submitter:); group:me is read as your group when you belong to exactly one, else the call fails naming your groups, so use group:<id>."),
|
|
31537
|
+
per_page: number$1().int().min(1).max(100).default(25).describe(PER_PAGE_DESC),
|
|
31538
|
+
page: number$1().int().min(1).default(1).describe(PAGE_DESC)
|
|
31539
|
+
}),
|
|
31540
|
+
annotations: {
|
|
31541
|
+
readOnlyHint: true,
|
|
31542
|
+
destructiveHint: false,
|
|
31543
|
+
idempotentHint: true,
|
|
31544
|
+
openWorldHint: true
|
|
31545
|
+
},
|
|
31546
|
+
handler: async (params, { caller }) => {
|
|
31547
|
+
const { query, per_page, page } = params;
|
|
31548
|
+
const token = await caller.getToken();
|
|
31549
|
+
const resolved = await resolveCallerGroupMe(query, subdomain, token, caller.sla);
|
|
31550
|
+
const response = await zendeskGet(subdomain, token, "/search", {
|
|
31551
|
+
query: resolved.query,
|
|
31552
|
+
...buildOffsetParams(per_page, page)
|
|
31553
|
+
});
|
|
31554
|
+
const results = response.results ?? [];
|
|
31555
|
+
const meta = extractSearchPaginationMeta(response, per_page, page);
|
|
31556
|
+
const header = (m) => [resolved.note, `Total: ${m.count} | Page ${page} (${results.length} results)${m.has_more ? ` | Next page: ${m.next_page}` : ""}`].filter(Boolean).join("\n");
|
|
31557
|
+
return { content: [{
|
|
31558
|
+
type: "text",
|
|
31559
|
+
text: formatFittingList(results, formatSearchResult, meta, offsetResume(page, per_page), header)
|
|
31560
|
+
}] };
|
|
31561
|
+
}
|
|
31562
|
+
}];
|
|
31563
|
+
};
|
|
31564
|
+
//#endregion
|
|
31565
|
+
//#region src/utils/elicitation.ts
|
|
31566
|
+
/**
|
|
31567
|
+
* Whether a client can answer a form elicitation. A bare `elicitation: {}`
|
|
31568
|
+
* predates the modes and counts as form support (spec rule the SDK follows).
|
|
31569
|
+
*/
|
|
31570
|
+
const supportsFormElicitation = (capabilities) => {
|
|
31571
|
+
const elicitation = capabilities?.elicitation;
|
|
31572
|
+
if (!elicitation) return false;
|
|
31573
|
+
return Object.keys(elicitation).length === 0 || "form" in elicitation;
|
|
31574
|
+
};
|
|
31575
|
+
const inputRequiredError = (result) => Object.assign(/* @__PURE__ */ new Error("The tool needs input from the user before it can answer."), {
|
|
31576
|
+
name: "InputRequiredError",
|
|
31577
|
+
inputRequired: result
|
|
31578
|
+
});
|
|
31579
|
+
const isInputRequiredError = (err) => err instanceof Error && err.name === "InputRequiredError" && "inputRequired" in err;
|
|
31546
31580
|
/** Where the thresholds in force come from, as the report states it. */
|
|
31547
31581
|
const thresholdsSource = (sla, prefs, overridden) => {
|
|
31548
31582
|
if (overridden) return "overridden for this call";
|
|
@@ -32244,7 +32278,7 @@ const createTicketTools = (env) => {
|
|
|
32244
32278
|
title: "Search Zendesk Tickets",
|
|
32245
32279
|
description: `Search tickets using Zendesk query syntax, returning each result with its live SLA state (per-metric stage and breach countdown) when an SLA policy applies. Examples: "status:open assignee:me", "priority:urgent ticket_type:incident". Returns total count, so queue triage like "breaching today" works without a per-ticket fetch. When the operator turned SLA push on, each SLA block also carries an Urgency line (BREACHED, CRITICAL or WARNING) against the caller's alert thresholds. ${EXCERPT_NOTE}`,
|
|
32246
32280
|
inputSchema: object({
|
|
32247
|
-
query: string().min(1).describe("Zendesk ticket search query — field filters like \"status:open\", \"assignee:me\", \"priority:urgent ticket_type:incident\", combined with free text. A \"type:ticket\" scope is added automatically, so filter the ticket kind with ticket_type: (e.g. ticket_type:incident), never type: (which the API rejects here)."),
|
|
32281
|
+
query: string().min(1).describe("Zendesk ticket search query — field filters like \"status:open\", \"assignee:me\", \"priority:urgent ticket_type:incident\", combined with free text. A \"type:ticket\" scope is added automatically, so filter the ticket kind with ticket_type: (e.g. ticket_type:incident), never type: (which the API rejects here). \"me\" is a value of the user keywords only (assignee:, requester:, submitter:); group:me is read as your group when you belong to exactly one, else the call fails naming your groups, so use group:<id>."),
|
|
32248
32282
|
per_page: number$1().int().min(1).max(100).default(25).describe(PER_PAGE_DESC),
|
|
32249
32283
|
page: number$1().int().min(1).default(1).describe(PAGE_DESC)
|
|
32250
32284
|
}),
|
|
@@ -32257,15 +32291,22 @@ const createTicketTools = (env) => {
|
|
|
32257
32291
|
handler: async (params, { caller }) => {
|
|
32258
32292
|
const { query, per_page, page } = params;
|
|
32259
32293
|
const token = await caller.getToken();
|
|
32260
|
-
const
|
|
32261
|
-
query
|
|
32262
|
-
|
|
32263
|
-
|
|
32264
|
-
|
|
32294
|
+
const search = async () => {
|
|
32295
|
+
const resolved = await resolveCallerGroupMe(query, subdomain, token, caller.sla);
|
|
32296
|
+
return {
|
|
32297
|
+
resolved,
|
|
32298
|
+
response: await zendeskGet(subdomain, token, "/search", {
|
|
32299
|
+
query: `type:ticket ${resolved.query}`,
|
|
32300
|
+
include: "tickets(slas)",
|
|
32301
|
+
...buildOffsetParams(per_page, page)
|
|
32302
|
+
})
|
|
32303
|
+
};
|
|
32304
|
+
};
|
|
32305
|
+
const [{ resolved, response }, urgency] = await Promise.all([search(), urgencyRenderer(caller.sla)]);
|
|
32265
32306
|
const formatTicketWithSla = (ticket) => formatTicketSummary(ticket) + formatSlaBlock(ticket.slas, urgency(ticket.slas));
|
|
32266
32307
|
return { content: [{
|
|
32267
32308
|
type: "text",
|
|
32268
|
-
text: formatFittingList(response.results ?? [], formatTicketWithSla, extractSearchPaginationMeta(response, per_page, page), offsetResume(page, per_page))
|
|
32309
|
+
text: formatFittingList(response.results ?? [], formatTicketWithSla, extractSearchPaginationMeta(response, per_page, page), offsetResume(page, per_page), (meta) => [resolved.note, formatPagination(meta)].filter(Boolean).join("\n"))
|
|
32269
32310
|
}] };
|
|
32270
32311
|
}
|
|
32271
32312
|
},
|
|
@@ -35136,7 +35177,7 @@ const main = async () => {
|
|
|
35136
35177
|
logger,
|
|
35137
35178
|
cleanup: async () => (await starting?.catch(() => void 0))?.close()
|
|
35138
35179
|
});
|
|
35139
|
-
const { startHttpTransport } = await import("./http-
|
|
35180
|
+
const { startHttpTransport } = await import("./http-DkjvhtQj.js");
|
|
35140
35181
|
starting = startHttpTransport(config, logger);
|
|
35141
35182
|
await starting;
|
|
35142
35183
|
};
|
|
@@ -35146,4 +35187,4 @@ main().catch((error) => {
|
|
|
35146
35187
|
process.exit(1);
|
|
35147
35188
|
});
|
|
35148
35189
|
//#endregion
|
|
35149
|
-
export {
|
|
35190
|
+
export { STARTUP_DOCS as A, WebStandardStreamableHTTPServerTransport as C, OAuthError as D, verifyBearerToken as E, errnoCode as M, OAuthErrorCode as O, DEFAULT_MAX_REQUEST_BODY_SIZE as S, isLegacyRequest as T, zendeskGet as _, toolEnv as a, DEFAULT_CALLBACK_PORT as b, clearToken as c, resolveTokenPath as d, saveToken as f, createAllTools as g, silentLogger as h, defaultServerFactory as i, createStartupError as j, compareCodeUnits as k, configDir as l, createRequestStateCodec as m, listenedUris as n, watchGrantAlerts as o, signInRequiredError as p, createServerCaches as r, startSlaRuntime as s, createListenFeed as t, loadToken as u, REQUEST_TIMEOUT_MS as v, createMcpHandler as w, getOAuthUrls as x, deadlineSignal as y };
|
package/dist/sbom.cdx.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:dbd14c1f-e90e-5bea-b648-7853fb8ee967",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
7
|
"component": {
|
|
8
8
|
"type": "application",
|
|
9
|
-
"bom-ref": "pkg:npm/%40fruggr/zendesk-mcp-server@3.
|
|
9
|
+
"bom-ref": "pkg:npm/%40fruggr/zendesk-mcp-server@3.6.0",
|
|
10
10
|
"name": "@fruggr/zendesk-mcp-server",
|
|
11
|
-
"version": "3.
|
|
12
|
-
"purl": "pkg:npm/%40fruggr/zendesk-mcp-server@3.
|
|
11
|
+
"version": "3.6.0",
|
|
12
|
+
"purl": "pkg:npm/%40fruggr/zendesk-mcp-server@3.6.0"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"components": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fruggr/zendesk-mcp-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"mcpName": "io.github.fruggr/zendesk-mcp-server",
|
|
5
5
|
"description": "Deep Zendesk MCP server for your AI assistant: search, draft, update and translate Help Center articles and manage Support tickets end to end — comments, triage and image attachments.",
|
|
6
6
|
"type": "module",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"dev:http": "tsx src/index.ts --transport http --host 127.0.0.1 --port 3000 --public-url http://localhost:3000 --mode all --namespace tickets --namespace help_center --namespace users --namespace requests",
|
|
22
22
|
"prepare": "npm run build && (lefthook install || true)",
|
|
23
23
|
"prepublishOnly": "npm run build",
|
|
24
|
-
"check": "
|
|
25
|
-
"check:fix": "
|
|
24
|
+
"check": "biome check --error-on-warnings .",
|
|
25
|
+
"check:fix": "biome check --write .",
|
|
26
26
|
"test": "vitest run",
|
|
27
27
|
"test:watch": "vitest watch",
|
|
28
28
|
"test:coverage": "vitest run --coverage",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"packageManager": "pnpm@12.15.0+sha512.b3951d6d6ff4a9f13dc7cf56cd28d2b8eb37dbcc9c68e52e1e1191873ea9f82a017287ab50baadc5ccc494f036c67c0e47eb13608c2fa36f47711b61ea128e61",
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@biomejs/biome": "2.
|
|
78
|
+
"@biomejs/biome": "2.6.2",
|
|
79
79
|
"@modelcontextprotocol/client": "2.3.1",
|
|
80
80
|
"@modelcontextprotocol/node": "2.1.1",
|
|
81
81
|
"@modelcontextprotocol/server": "2.3.1",
|