@fruggr/zendesk-mcp-server 2.5.0 → 2.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/index.js +81 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -960,6 +960,8 @@ const formatList = (items, formatter, meta) => {
|
|
|
960
960
|
};
|
|
961
961
|
//#endregion
|
|
962
962
|
//#region src/utils/pagination.ts
|
|
963
|
+
const PER_PAGE_DESC = "Number of results per page for offset pagination (1-100). Pair with `page` to walk large result sets; the response header reports the total count and whether more pages remain.";
|
|
964
|
+
const PAGE_DESC = "1-based page number for offset pagination. Increment it while keeping `per_page` fixed to fetch subsequent pages; page 1 is the first page.";
|
|
963
965
|
const buildCursorParams = (pageSize, cursor) => {
|
|
964
966
|
const params = { "page[size]": String(pageSize) };
|
|
965
967
|
if (cursor) params["page[after]"] = cursor;
|
|
@@ -1207,6 +1209,7 @@ const markdownToHtml = (markdown) => {
|
|
|
1207
1209
|
};
|
|
1208
1210
|
//#endregion
|
|
1209
1211
|
//#region src/tools/help-center.ts
|
|
1212
|
+
const ARTICLE_ID_DESC = "Article ID — the numeric id of the Help Center article. Obtain it from list_articles or search_articles.";
|
|
1210
1213
|
const largeArticleHint = (body, sectionCount) => {
|
|
1211
1214
|
if (body.length < 3e3 && sectionCount < 4) return null;
|
|
1212
1215
|
return [
|
|
@@ -1226,10 +1229,10 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1226
1229
|
title: "Search Help Center Articles",
|
|
1227
1230
|
description: "Full-text search across Help Center articles (metadata only, no body). Use get_article for full content. Supports locale filtering. Returns total count.",
|
|
1228
1231
|
inputSchema: z.object({
|
|
1229
|
-
query: z.string().min(1).describe("
|
|
1232
|
+
query: z.string().min(1).describe("Full-text query matched against article titles and body. Plain keywords; combine with the locale filter to scope to one language."),
|
|
1230
1233
|
locale: z.string().optional().describe("Filter by locale (e.g., \"en-us\", \"fr\")"),
|
|
1231
|
-
per_page: z.number().int().min(1).max(100).default(100).describe(
|
|
1232
|
-
page: z.number().int().min(1).default(1).describe(
|
|
1234
|
+
per_page: z.number().int().min(1).max(100).default(100).describe(PER_PAGE_DESC),
|
|
1235
|
+
page: z.number().int().min(1).default(1).describe(PAGE_DESC)
|
|
1233
1236
|
}),
|
|
1234
1237
|
annotations: {
|
|
1235
1238
|
readOnlyHint: true,
|
|
@@ -1259,7 +1262,7 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1259
1262
|
title: "Get Help Center Article",
|
|
1260
1263
|
description: "Retrieve an article by ID with full body content. For large articles, prefer get_article_outline + get_article_section to save tokens. Optionally specify locale for a translated version. Returns body (HTML), metadata, source_locale, and list of available translations.",
|
|
1261
1264
|
inputSchema: z.object({
|
|
1262
|
-
article_id: z.number().int().describe(
|
|
1265
|
+
article_id: z.number().int().describe(ARTICLE_ID_DESC),
|
|
1263
1266
|
locale: z.string().optional().describe("Locale for translated version")
|
|
1264
1267
|
}),
|
|
1265
1268
|
annotations: {
|
|
@@ -1341,8 +1344,8 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1341
1344
|
title: "List Help Center Articles",
|
|
1342
1345
|
description: "List articles (metadata only, no body). Use get_article for full content. Optionally filter by section ID and locale. Supports sort_by (\"title\", \"created_at\", \"updated_at\") and include_translations: true to show available translation locales per article. Note: include_translations must be re-sent on each paginated request.",
|
|
1343
1346
|
inputSchema: z.object({
|
|
1344
|
-
section_id: z.number().int().optional(),
|
|
1345
|
-
locale: z.string().optional(),
|
|
1347
|
+
section_id: z.number().int().optional().describe("Restrict the listing to one section (numeric id from list_sections). Omit to list articles across all sections."),
|
|
1348
|
+
locale: z.string().optional().describe("Restrict to a single locale, e.g. \"en-us\" or \"fr\". Omit for the default locale."),
|
|
1346
1349
|
page_size: z.number().int().min(1).max(100).default(100).describe("Articles per page (1-100, default 100)."),
|
|
1347
1350
|
cursor: z.string().optional().describe("Pagination cursor from a previous response; omit for the first page."),
|
|
1348
1351
|
sort_by: z.enum([
|
|
@@ -1350,8 +1353,8 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1350
1353
|
"updated_at",
|
|
1351
1354
|
"position",
|
|
1352
1355
|
"title"
|
|
1353
|
-
]).default("position").describe("
|
|
1354
|
-
sort_order: z.enum(["asc", "desc"]).default("asc").describe("Sort direction"),
|
|
1356
|
+
]).default("position").describe("Field to sort by; \"position\" (the default) is the manual order set in Guide."),
|
|
1357
|
+
sort_order: z.enum(["asc", "desc"]).default("asc").describe("Sort direction: ascending or descending."),
|
|
1355
1358
|
include_translations: z.boolean().default(false).describe("Include available translation locales per article (causes 1 extra API call per article)")
|
|
1356
1359
|
}),
|
|
1357
1360
|
annotations: {
|
|
@@ -1391,7 +1394,7 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1391
1394
|
readOnly: true,
|
|
1392
1395
|
title: "List Article Translations",
|
|
1393
1396
|
description: "List all available translations for an article (metadata only, no body: locale, title, draft, updated_at). Use get_article with locale for full translated content.",
|
|
1394
|
-
inputSchema: z.object({ article_id: z.number().int().describe(
|
|
1397
|
+
inputSchema: z.object({ article_id: z.number().int().describe(ARTICLE_ID_DESC) }),
|
|
1395
1398
|
annotations: {
|
|
1396
1399
|
readOnlyHint: true,
|
|
1397
1400
|
destructiveHint: false,
|
|
@@ -1447,11 +1450,11 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1447
1450
|
title: "Update Article Translation",
|
|
1448
1451
|
description: "Update article content (title, body) in a specific locale. For targeted edits on one or a few sections, prefer update_article_section — this tool replaces the FULL body and re-sends the entire article on each write. Use the article's source_locale (from get_article) for the default language, or another locale for translations.",
|
|
1449
1452
|
inputSchema: z.object({
|
|
1450
|
-
article_id: z.number().int(),
|
|
1451
|
-
locale: z.string(),
|
|
1452
|
-
title: z.string().optional(),
|
|
1453
|
-
body: z.string().optional(),
|
|
1454
|
-
draft: z.boolean().optional()
|
|
1453
|
+
article_id: z.number().int().describe("Article ID — the numeric id of the article whose translation to update. Obtain it from list_articles or search_articles."),
|
|
1454
|
+
locale: z.string().describe("Locale of the translation to update, e.g. \"en-us\" or \"fr\". Use the source_locale (from get_article) to edit the default language."),
|
|
1455
|
+
title: z.string().optional().describe("New title for this locale. Omit to leave the current title unchanged."),
|
|
1456
|
+
body: z.string().optional().describe("New full body (HTML) for this locale. Replaces the entire body — for a single-section edit prefer update_article_section. Omit to leave the body unchanged."),
|
|
1457
|
+
draft: z.boolean().optional().describe("When true, keeps this translation as a draft; when false, publishes it.")
|
|
1455
1458
|
}),
|
|
1456
1459
|
annotations: {
|
|
1457
1460
|
readOnlyHint: false,
|
|
@@ -1495,16 +1498,16 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1495
1498
|
title: "Create Help Center Article",
|
|
1496
1499
|
description: "Create a new article in a section. The locale becomes the article's source_locale. Requires a permission_group_id (use list_permission_groups to find available IDs). To add content in other locales afterwards, use create_article_translation.",
|
|
1497
1500
|
inputSchema: z.object({
|
|
1498
|
-
section_id: z.number().int(),
|
|
1499
|
-
title: z.string().min(1),
|
|
1500
|
-
body: z.string().min(1).describe("Article body (
|
|
1501
|
+
section_id: z.number().int().describe("Section that will contain the article (numeric id from list_sections)."),
|
|
1502
|
+
title: z.string().min(1).describe("Title of the new article, in its source locale."),
|
|
1503
|
+
body: z.string().min(1).describe("Article body as HTML (this becomes the source-locale content)."),
|
|
1501
1504
|
permission_group_id: z.number().int().describe("Permission group ID (use list_permission_groups to find it)"),
|
|
1502
1505
|
user_segment_id: z.number().int().optional().describe("User segment ID for visibility (use list_user_segments to find it). Defaults to everyone."),
|
|
1503
1506
|
author_id: z.number().int().optional().describe("Author user ID. Defaults to the authenticated user."),
|
|
1504
1507
|
content_tag_ids: z.array(z.string()).optional().describe("Content tag IDs (use list_content_tags to find them)"),
|
|
1505
|
-
locale: z.string().optional(),
|
|
1506
|
-
draft: z.boolean().default(true),
|
|
1507
|
-
promoted: z.boolean().default(false),
|
|
1508
|
+
locale: z.string().optional().describe("Source locale for the article, e.g. \"en-us\" or \"fr\". Defaults to the Help Center's default locale; becomes the article's source_locale."),
|
|
1509
|
+
draft: z.boolean().default(true).describe("When true (default), the article is created unpublished; set false to publish immediately."),
|
|
1510
|
+
promoted: z.boolean().default(false).describe("When true, marks the article as promoted (featured) in its section. Defaults to false."),
|
|
1508
1511
|
label_names: z.array(z.string()).optional().describe("Label names for search ranking (use list_labels to see existing labels)")
|
|
1509
1512
|
}),
|
|
1510
1513
|
annotations: {
|
|
@@ -1529,15 +1532,15 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1529
1532
|
title: "Update Help Center Article",
|
|
1530
1533
|
description: "Update article metadata only (draft, promoted, labels, tags, visibility, section, sort position, etc.). Does NOT update content (title, body) — use update_article_translation for that.",
|
|
1531
1534
|
inputSchema: z.object({
|
|
1532
|
-
article_id: z.number().int(),
|
|
1533
|
-
draft: z.boolean().optional(),
|
|
1534
|
-
promoted: z.boolean().optional(),
|
|
1535
|
-
label_names: z.array(z.string()).optional().describe("Label names for search ranking"),
|
|
1536
|
-
content_tag_ids: z.array(z.string()).optional().describe("Content tag
|
|
1537
|
-
user_segment_id: z.number().int().optional().describe("User segment
|
|
1538
|
-
author_id: z.number().int().optional().describe("
|
|
1539
|
-
permission_group_id: z.number().int().optional().describe("
|
|
1540
|
-
section_id: z.number().int().optional(),
|
|
1535
|
+
article_id: z.number().int().describe("Article ID — the numeric id of the article to update. Obtain it from list_articles or search_articles."),
|
|
1536
|
+
draft: z.boolean().optional().describe("Set true to unpublish the article (revert to draft) or false to publish it."),
|
|
1537
|
+
promoted: z.boolean().optional().describe("Set true to promote (feature) the article in its section, or false to unpromote it."),
|
|
1538
|
+
label_names: z.array(z.string()).optional().describe("Label names for search ranking (use list_labels to see existing labels)."),
|
|
1539
|
+
content_tag_ids: z.array(z.string()).optional().describe("Content tag ids to attach (use list_content_tags to find them)."),
|
|
1540
|
+
user_segment_id: z.number().int().optional().describe("User segment that controls who can see the article (id from list_user_segments)."),
|
|
1541
|
+
author_id: z.number().int().optional().describe("User id of the article author (from search_users)."),
|
|
1542
|
+
permission_group_id: z.number().int().optional().describe("Guide permission group controlling who can edit (id from list_permission_groups)."),
|
|
1543
|
+
section_id: z.number().int().optional().describe("Move the article to this section (numeric id from list_sections)."),
|
|
1541
1544
|
position: z.number().int().min(0).optional().describe("Sort position within the section (manual ordering only; 0 = first/top). New articles default to position 0. To move an article to the END of its section, set this to one more than the highest current position: read the highest position P from list_articles with sort_by=\"position\", sort_order=\"desc\", then set position = P + 1.")
|
|
1542
1545
|
}),
|
|
1543
1546
|
annotations: {
|
|
@@ -1652,9 +1655,14 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1652
1655
|
},
|
|
1653
1656
|
handler: async (params) => {
|
|
1654
1657
|
const { article_id } = params;
|
|
1658
|
+
const attachments = (await helpCenterGet(subdomain, await getToken(), `/articles/${article_id}/attachments`)).article_attachments ?? [];
|
|
1659
|
+
if (attachments.length === 0) return { content: [{
|
|
1660
|
+
type: "text",
|
|
1661
|
+
text: `No attachments found on article #${article_id}.`
|
|
1662
|
+
}] };
|
|
1655
1663
|
return { content: [{
|
|
1656
1664
|
type: "text",
|
|
1657
|
-
text: formatList(
|
|
1665
|
+
text: formatList(attachments, formatAttachment)
|
|
1658
1666
|
}] };
|
|
1659
1667
|
}
|
|
1660
1668
|
},
|
|
@@ -1665,7 +1673,7 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1665
1673
|
title: "Get Article Outline",
|
|
1666
1674
|
description: "Return a compact outline of an article (list of sections delimited by h1/h2/h3, with word counts) for the given locale (defaults to source_locale). Includes available translations with their outdated status. Use get_article_section to fetch a specific section.",
|
|
1667
1675
|
inputSchema: z.object({
|
|
1668
|
-
article_id: z.number().int().describe(
|
|
1676
|
+
article_id: z.number().int().describe(ARTICLE_ID_DESC),
|
|
1669
1677
|
locale: z.string().optional().describe("Locale of the body to outline (defaults to article source_locale)")
|
|
1670
1678
|
}),
|
|
1671
1679
|
annotations: {
|
|
@@ -1706,7 +1714,7 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1706
1714
|
title: "Get Article Section",
|
|
1707
1715
|
description: "Retrieve the content of a single section of an article in a given locale. Use get_article_outline first to discover section indexes. Default format=\"html\" for round-trip safety. Pass format=\"markdown\" only for human review — the Markdown representation is lossy on some structures (<pre> with <br>, tables with multi-<p> cells are kept as raw HTML to limit the damage, but do not round-trip markdown content back through update_article_section).",
|
|
1708
1716
|
inputSchema: z.object({
|
|
1709
|
-
article_id: z.number().int().describe(
|
|
1717
|
+
article_id: z.number().int().describe(ARTICLE_ID_DESC),
|
|
1710
1718
|
locale: z.string().describe("Locale of the body (e.g., \"en-us\", \"fr\")"),
|
|
1711
1719
|
section_index: z.number().int().min(0).describe("0-based index of the section (see get_article_outline)"),
|
|
1712
1720
|
format: z.enum(["html", "markdown"]).default("html").describe("Output format. \"html\" (default) is round-trip safe. \"markdown\" is lossy on some HTML structures — use only for human review, not before update_article_section.")
|
|
@@ -1742,7 +1750,7 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1742
1750
|
title: "Update Article Section",
|
|
1743
1751
|
description: "Replace the content of a single section of an article in a given locale, keeping the rest of the body intact. The server fetches the current body, replaces the targeted section, and PUTs the full reconstructed body via the Translations API. Default format=\"html\" for fidelity. Use format=\"markdown\" only when you control the input and know it does not rely on structures that round-trip poorly (code blocks with line breaks, tables with multi-paragraph cells). The section heading is preserved and is NOT part of the replaced content.",
|
|
1744
1752
|
inputSchema: z.object({
|
|
1745
|
-
article_id: z.number().int().describe(
|
|
1753
|
+
article_id: z.number().int().describe(ARTICLE_ID_DESC),
|
|
1746
1754
|
locale: z.string().describe("Locale of the translation to update"),
|
|
1747
1755
|
section_index: z.number().int().min(0).describe("0-based index of the section to replace (see get_article_outline)"),
|
|
1748
1756
|
content: z.string().describe("New content for the section (heading excluded). HTML by default, Markdown if format=\"markdown\"."),
|
|
@@ -1776,8 +1784,8 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1776
1784
|
title: "Compare Article Translations",
|
|
1777
1785
|
description: "Compare section structure between two locales of the same article, matched by index. Returns a compact table (one row per section) with status: \"ok\" (both present, source/target word count ratio within 25%), \"different\" (word count ratio diverges by more than 25% — size signal only, NOT a semantic divergence: two locales may legitimately differ in verbosity) or \"missing\" (section absent in target). Useful to spot structurally stale or missing sections; do not interpret \"different\" as an edit regression on its own.",
|
|
1778
1786
|
inputSchema: z.object({
|
|
1779
|
-
article_id: z.number().int().describe(
|
|
1780
|
-
source_locale: z.string().describe("
|
|
1787
|
+
article_id: z.number().int().describe(ARTICLE_ID_DESC),
|
|
1788
|
+
source_locale: z.string().describe("Reference locale to diff against, e.g. \"en-us\". Usually the article source_locale (from get_article)."),
|
|
1781
1789
|
target_locale: z.string().describe("Target locale to compare against source")
|
|
1782
1790
|
}),
|
|
1783
1791
|
annotations: {
|
|
@@ -1828,7 +1836,7 @@ const createHelpCenterTools = (ctx) => {
|
|
|
1828
1836
|
title: "Create Article Attachment",
|
|
1829
1837
|
description: "Upload an attachment to an article. Provide file content as base64-encoded string.",
|
|
1830
1838
|
inputSchema: z.object({
|
|
1831
|
-
article_id: z.number().int().describe(
|
|
1839
|
+
article_id: z.number().int().describe(ARTICLE_ID_DESC),
|
|
1832
1840
|
file_name: z.string().min(1).describe("File name (e.g., \"screenshot.png\")"),
|
|
1833
1841
|
file_base64: z.string().min(1).describe("File content encoded as base64"),
|
|
1834
1842
|
content_type: z.string().default("application/octet-stream").describe("MIME type (e.g., \"image/png\", \"application/pdf\")")
|
|
@@ -1879,9 +1887,9 @@ const createSearchTools = (ctx) => {
|
|
|
1879
1887
|
title: "Zendesk Unified Search",
|
|
1880
1888
|
description: "Search across tickets, users, and organizations. Supports filters like \"type:ticket status:open\", \"type:user role:agent\". Returns total count and paginated results (100 per page). Organization results include name and ID only — use get_organization for full details (tags, domains, details).",
|
|
1881
1889
|
inputSchema: z.object({
|
|
1882
|
-
query: z.string().min(1).describe("Zendesk search query"),
|
|
1883
|
-
per_page: z.number().int().min(1).max(100).default(100).describe(
|
|
1884
|
-
page: z.number().int().min(1).default(1).describe(
|
|
1890
|
+
query: z.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."),
|
|
1891
|
+
per_page: z.number().int().min(1).max(100).default(100).describe(PER_PAGE_DESC),
|
|
1892
|
+
page: z.number().int().min(1).default(1).describe(PAGE_DESC)
|
|
1885
1893
|
}),
|
|
1886
1894
|
annotations: {
|
|
1887
1895
|
readOnlyHint: true,
|
|
@@ -2012,8 +2020,8 @@ const createTicketTools = (ctx) => {
|
|
|
2012
2020
|
title: "Get Zendesk Ticket",
|
|
2013
2021
|
description: "Retrieve a Zendesk ticket by ID, including its live SLA state (per-metric stage and breach countdown) when an SLA policy applies, plus its comments if requested. Returns ticket details (subject, status, priority, assignee, tags, description) and optionally all comments/internal notes. The per-ticket Show endpoint exposes no SLA, so the SLA block is resolved via a scoped search and may be absent for a very high-volume requester or a just-updated ticket; SLA targets and policy conditions live in list_sla_policies.",
|
|
2014
2022
|
inputSchema: z.object({
|
|
2015
|
-
ticket_id: z.number().int().describe("Ticket ID"),
|
|
2016
|
-
include_comments: z.boolean().default(false).describe("
|
|
2023
|
+
ticket_id: z.number().int().describe("Ticket ID — the numeric id of the ticket to fetch. Obtain it from search_tickets or list_tickets."),
|
|
2024
|
+
include_comments: z.boolean().default(false).describe("When true, appends the full public comment and internal note thread to the response. Defaults to false to keep the payload small; enable it when you need the conversation, not just the ticket fields.")
|
|
2017
2025
|
}),
|
|
2018
2026
|
annotations: {
|
|
2019
2027
|
readOnlyHint: true,
|
|
@@ -2043,7 +2051,7 @@ const createTicketTools = (ctx) => {
|
|
|
2043
2051
|
title: "Get Zendesk Ticket Attachments",
|
|
2044
2052
|
description: "Retrieve ticket attachments. Images are embedded inline; other files are listed as text references.",
|
|
2045
2053
|
inputSchema: z.object({
|
|
2046
|
-
ticket_id: z.number().int().describe("Ticket ID"),
|
|
2054
|
+
ticket_id: z.number().int().describe("Ticket ID — the numeric id of the ticket whose attachments to fetch. Obtain it from search_tickets or list_tickets."),
|
|
2047
2055
|
attachment_ids: z.array(z.number().int()).optional().describe("Attachment IDs to fetch directly (e.g. extracted from a previous get_ticket(include_comments=true) call). When provided, skips the comments fetch entirely. When omitted, all attachments of the ticket are returned.")
|
|
2048
2056
|
}),
|
|
2049
2057
|
annotations: {
|
|
@@ -2081,11 +2089,11 @@ const createTicketTools = (ctx) => {
|
|
|
2081
2089
|
namespace: "tickets",
|
|
2082
2090
|
readOnly: true,
|
|
2083
2091
|
title: "Search Zendesk Tickets",
|
|
2084
|
-
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
|
|
2092
|
+
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.",
|
|
2085
2093
|
inputSchema: z.object({
|
|
2086
|
-
query: z.string().min(1).describe("Zendesk search query
|
|
2087
|
-
per_page: z.number().int().min(1).max(100).default(100).describe(
|
|
2088
|
-
page: z.number().int().min(1).default(1).describe(
|
|
2094
|
+
query: z.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)."),
|
|
2095
|
+
per_page: z.number().int().min(1).max(100).default(100).describe(PER_PAGE_DESC),
|
|
2096
|
+
page: z.number().int().min(1).default(1).describe(PAGE_DESC)
|
|
2089
2097
|
}),
|
|
2090
2098
|
annotations: {
|
|
2091
2099
|
readOnlyHint: true,
|
|
@@ -2114,8 +2122,8 @@ const createTicketTools = (ctx) => {
|
|
|
2114
2122
|
title: "Create Zendesk Ticket",
|
|
2115
2123
|
description: "Create a new Zendesk support ticket with subject, description, and optional priority/type/assignee/tags. The description becomes the first public comment of the ticket, and the new ticket id is returned. After creation, use update_ticket to change status or assignee, add_public_comment or add_private_note to reply, and manage_tags to adjust tags. Look up valid assignee_id / group_id and custom field ids via search_users or your Zendesk admin settings.",
|
|
2116
2124
|
inputSchema: z.object({
|
|
2117
|
-
subject: z.string().min(1).describe("Ticket subject"),
|
|
2118
|
-
description: z.string().min(1).describe("Ticket description"),
|
|
2125
|
+
subject: z.string().min(1).describe("Ticket subject — the short summary line shown in ticket lists and search results."),
|
|
2126
|
+
description: z.string().min(1).describe("Ticket description — the body of the request. It becomes the ticket's first public comment (visible to the requester)."),
|
|
2119
2127
|
priority: z.enum([
|
|
2120
2128
|
"urgent",
|
|
2121
2129
|
"high",
|
|
@@ -2130,7 +2138,7 @@ const createTicketTools = (ctx) => {
|
|
|
2130
2138
|
]).optional().describe("Ticket type. One of problem, incident, question, task."),
|
|
2131
2139
|
assignee_id: z.number().int().optional().describe("User id of the agent to assign the ticket to."),
|
|
2132
2140
|
group_id: z.number().int().optional().describe("Id of the group to assign the ticket to."),
|
|
2133
|
-
tags: z.array(z.string()).optional().describe("Tags to set on the ticket."),
|
|
2141
|
+
tags: z.array(z.string()).optional().describe("Tags to set on the new ticket. Each tag is a single lowercase token (join multi-word tags with an underscore). Use manage_tags later to add or remove individual tags."),
|
|
2134
2142
|
custom_fields: z.array(z.object({
|
|
2135
2143
|
id: z.number().int(),
|
|
2136
2144
|
value: z.unknown()
|
|
@@ -2162,7 +2170,7 @@ const createTicketTools = (ctx) => {
|
|
|
2162
2170
|
title: "Update Zendesk Ticket",
|
|
2163
2171
|
description: "Update an existing ticket (status, priority, type, assignee, group, subject, tags, custom fields). Only the fields you pass are changed, and the updated ticket is returned. Setting tags here replaces the whole tag set — use manage_tags to add or remove individual tags without overwriting the rest. This tool does not post replies: use add_public_comment or add_private_note for that. Find the ticket id via search_tickets or list_tickets.",
|
|
2164
2172
|
inputSchema: z.object({
|
|
2165
|
-
ticket_id: z.number().int().describe("Ticket ID"),
|
|
2173
|
+
ticket_id: z.number().int().describe("Ticket ID — the numeric id of the ticket to update. Obtain it from search_tickets or list_tickets."),
|
|
2166
2174
|
status: z.enum([
|
|
2167
2175
|
"new",
|
|
2168
2176
|
"open",
|
|
@@ -2185,7 +2193,7 @@ const createTicketTools = (ctx) => {
|
|
|
2185
2193
|
]).optional().describe("Ticket type. One of problem, incident, question, task."),
|
|
2186
2194
|
assignee_id: z.number().int().optional().describe("User id of the agent to assign the ticket to."),
|
|
2187
2195
|
group_id: z.number().int().optional().describe("Id of the group to assign the ticket to."),
|
|
2188
|
-
subject: z.string().optional().describe("New ticket subject
|
|
2196
|
+
subject: z.string().optional().describe("New subject line for the ticket; replaces the current subject when provided."),
|
|
2189
2197
|
tags: z.array(z.string()).optional().describe("Replaces the full tag set on the ticket. Use manage_tags for incremental add/remove."),
|
|
2190
2198
|
custom_fields: z.array(z.object({
|
|
2191
2199
|
id: z.number().int(),
|
|
@@ -2212,10 +2220,10 @@ const createTicketTools = (ctx) => {
|
|
|
2212
2220
|
namespace: "tickets",
|
|
2213
2221
|
readOnly: false,
|
|
2214
2222
|
title: "Add Private Note",
|
|
2215
|
-
description: "Add an internal note (not visible to requester) to a ticket, optionally with file attachments (uploaded via the Zendesk Uploads API and carried on the note).",
|
|
2223
|
+
description: "Add an internal note (not visible to requester) to a ticket, optionally with file attachments (uploaded via the Zendesk Uploads API and carried on the note). The note is appended to the ticket thread; use add_public_comment instead when the reply should be visible to the requester.",
|
|
2216
2224
|
inputSchema: z.object({
|
|
2217
|
-
ticket_id: z.number().int().describe("Ticket ID"),
|
|
2218
|
-
body: z.string().min(1).describe("Note
|
|
2225
|
+
ticket_id: z.number().int().describe("Ticket ID — the numeric id of the ticket to annotate. Obtain it from search_tickets or list_tickets."),
|
|
2226
|
+
body: z.string().min(1).describe("Note text (internal, agent-only). Plain text or HTML; not shown to the requester."),
|
|
2219
2227
|
attachments: z.array(attachmentSchema).optional().describe("Files to attach to this note (base64-encoded content).")
|
|
2220
2228
|
}),
|
|
2221
2229
|
annotations: {
|
|
@@ -2244,10 +2252,10 @@ const createTicketTools = (ctx) => {
|
|
|
2244
2252
|
namespace: "tickets",
|
|
2245
2253
|
readOnly: false,
|
|
2246
2254
|
title: "Add Public Comment",
|
|
2247
|
-
description: "Add a public comment (visible to requester) to a ticket, optionally with file attachments (uploaded via the Zendesk Uploads API and carried on the comment).",
|
|
2255
|
+
description: "Add a public comment (visible to requester) to a ticket, optionally with file attachments (uploaded via the Zendesk Uploads API and carried on the comment). The comment is appended to the ticket thread and emails the requester; use add_private_note instead for an internal, agent-only note.",
|
|
2248
2256
|
inputSchema: z.object({
|
|
2249
|
-
ticket_id: z.number().int().describe("Ticket ID"),
|
|
2250
|
-
body: z.string().min(1).describe("Comment
|
|
2257
|
+
ticket_id: z.number().int().describe("Ticket ID — the numeric id of the ticket to reply on. Obtain it from search_tickets or list_tickets."),
|
|
2258
|
+
body: z.string().min(1).describe("Comment text sent to the requester. Plain text or HTML; visible in the ticket."),
|
|
2251
2259
|
attachments: z.array(attachmentSchema).optional().describe("Files to attach to this comment (base64-encoded content).")
|
|
2252
2260
|
}),
|
|
2253
2261
|
annotations: {
|
|
@@ -2276,7 +2284,7 @@ const createTicketTools = (ctx) => {
|
|
|
2276
2284
|
namespace: "tickets",
|
|
2277
2285
|
readOnly: true,
|
|
2278
2286
|
title: "List Zendesk Tickets",
|
|
2279
|
-
description: "List tickets with cursor-based pagination,
|
|
2287
|
+
description: "List tickets with cursor-based pagination, in Zendesk's default order (ascending ticket id), not by recency. Page size is controlled by page_size (not per_page, which is the offset-based parameter used by search_tickets); paginate by passing the returned cursor. To find tickets by recency or any other criterion, use search_tickets with a query.",
|
|
2280
2288
|
inputSchema: z.object({
|
|
2281
2289
|
page_size: z.number().int().min(1).max(100).default(100).describe("Tickets per page (1-100, default 100)."),
|
|
2282
2290
|
cursor: z.string().optional().describe("Pagination cursor from a previous response; omit for the first page.")
|
|
@@ -2302,8 +2310,8 @@ const createTicketTools = (ctx) => {
|
|
|
2302
2310
|
namespace: "tickets",
|
|
2303
2311
|
readOnly: true,
|
|
2304
2312
|
title: "Get Linked Incidents",
|
|
2305
|
-
description: "Get all incident tickets linked to a problem ticket.",
|
|
2306
|
-
inputSchema: z.object({ problem_id: z.number().int().describe("Problem ticket ID") }),
|
|
2313
|
+
description: "Get all incident tickets linked to a problem ticket. Returns the list of incidents that reference the given problem (Zendesk problem/incident relationship); useful to gauge a problem's blast radius before resolving it.",
|
|
2314
|
+
inputSchema: z.object({ problem_id: z.number().int().describe("Problem ticket ID — the numeric id of the ticket of type \"problem\" whose linked incidents to list. Obtain it from search_tickets or list_tickets.") }),
|
|
2307
2315
|
annotations: {
|
|
2308
2316
|
readOnlyHint: true,
|
|
2309
2317
|
destructiveHint: false,
|
|
@@ -2324,11 +2332,11 @@ const createTicketTools = (ctx) => {
|
|
|
2324
2332
|
namespace: "tickets",
|
|
2325
2333
|
readOnly: false,
|
|
2326
2334
|
title: "Manage Ticket Tags",
|
|
2327
|
-
description: "Add or remove tags on a ticket.",
|
|
2335
|
+
description: "Add or remove tags on a ticket. Performs an incremental read-modify-write: it fetches the ticket's current tags, adds those in `add` and deletes those in `remove`, then saves the merged set — tags you don't list are left untouched and duplicates are collapsed. Adding a tag already present, or removing one that is absent, is a no-op (idempotent). Returns the ticket's full tag set after the update. Use this for incremental tag edits; to overwrite the entire tag set at once, or to change tags alongside other fields, use update_ticket instead. Find the ticket id via search_tickets or list_tickets.",
|
|
2328
2336
|
inputSchema: z.object({
|
|
2329
|
-
ticket_id: z.number().int().describe("Ticket ID"),
|
|
2330
|
-
add: z.array(z.string()).optional().describe("Tags to add"),
|
|
2331
|
-
remove: z.array(z.string()).optional().describe("Tags to remove")
|
|
2337
|
+
ticket_id: z.number().int().describe("Ticket ID — the numeric id of the ticket whose tags to modify. Obtain it from search_tickets or list_tickets."),
|
|
2338
|
+
add: z.array(z.string()).optional().describe("Tags to add. Zendesk tags are single tokens: a value containing spaces is stored as separate tags rather than one tag, so join multi-word tags yourself with an underscore or dash (e.g. \"urgent_request\"). Adding a tag already on the ticket is a no-op. Omit to only remove."),
|
|
2339
|
+
remove: z.array(z.string()).optional().describe("Tags to remove. Removing a tag that is not present is a no-op; tags not listed here stay in place. Omit to only add.")
|
|
2332
2340
|
}),
|
|
2333
2341
|
annotations: {
|
|
2334
2342
|
readOnlyHint: false,
|
|
@@ -2361,8 +2369,8 @@ const createTicketTools = (ctx) => {
|
|
|
2361
2369
|
title: "List SLA Policies",
|
|
2362
2370
|
description: "List the configured SLA policies with their filter conditions and per-priority reply/resolution targets. Use this to explain why a given target applies to a ticket and to reconstruct deadlines deterministically instead of hard-coding the policy matrix. Requires an admin token (or a custom role granted the SLA-management permission); a standard agent token gets 403 here, though it can still read live per-ticket SLA via get_ticket / search_tickets.",
|
|
2363
2371
|
inputSchema: z.object({
|
|
2364
|
-
per_page: z.number().int().min(1).max(100).default(100).describe(
|
|
2365
|
-
page: z.number().int().min(1).default(1).describe(
|
|
2372
|
+
per_page: z.number().int().min(1).max(100).default(100).describe(PER_PAGE_DESC),
|
|
2373
|
+
page: z.number().int().min(1).default(1).describe(PAGE_DESC)
|
|
2366
2374
|
}),
|
|
2367
2375
|
annotations: {
|
|
2368
2376
|
readOnlyHint: true,
|
|
@@ -2426,9 +2434,9 @@ const createUserTools = (ctx) => {
|
|
|
2426
2434
|
title: "Search Zendesk Users",
|
|
2427
2435
|
description: "Search for users by name, email, or other criteria using Zendesk search query syntax. Returns total count.",
|
|
2428
2436
|
inputSchema: z.object({
|
|
2429
|
-
query: z.string().min(1).describe("
|
|
2430
|
-
per_page: z.number().int().min(1).max(100).default(100).describe(
|
|
2431
|
-
page: z.number().int().min(1).default(1).describe(
|
|
2437
|
+
query: z.string().min(1).describe("Zendesk user search query — free text matched against name and email, and/or field filters like \"email:jane@acme.com\", \"role:agent\", \"organization_id:123\". A \"type:user\" scope is added automatically."),
|
|
2438
|
+
per_page: z.number().int().min(1).max(100).default(100).describe(PER_PAGE_DESC),
|
|
2439
|
+
page: z.number().int().min(1).default(1).describe(PAGE_DESC)
|
|
2432
2440
|
}),
|
|
2433
2441
|
annotations: {
|
|
2434
2442
|
readOnlyHint: true,
|
|
@@ -2453,8 +2461,8 @@ const createUserTools = (ctx) => {
|
|
|
2453
2461
|
namespace: "users",
|
|
2454
2462
|
readOnly: true,
|
|
2455
2463
|
title: "Get Zendesk User",
|
|
2456
|
-
description: "Retrieve a user by
|
|
2457
|
-
inputSchema: z.object({ user_id: z.number().int().describe("User ID") }),
|
|
2464
|
+
description: "Retrieve a single user by their numeric id. Returns the full user record (name, email, role, organization, tags). Use search_users when you only have a name or email, or get_current_user for the authenticated identity.",
|
|
2465
|
+
inputSchema: z.object({ user_id: z.number().int().describe("User ID — the numeric id of the Zendesk user to fetch. Obtain it from search_users, or from the requester/assignee fields of a ticket.") }),
|
|
2458
2466
|
annotations: {
|
|
2459
2467
|
readOnlyHint: true,
|
|
2460
2468
|
destructiveHint: false,
|
|
@@ -2475,8 +2483,8 @@ const createUserTools = (ctx) => {
|
|
|
2475
2483
|
namespace: "users",
|
|
2476
2484
|
readOnly: true,
|
|
2477
2485
|
title: "Get Zendesk Organization",
|
|
2478
|
-
description: "Retrieve
|
|
2479
|
-
inputSchema: z.object({ organization_id: z.number().int().describe("Organization ID") }),
|
|
2486
|
+
description: "Retrieve a single organization by its numeric id. Returns full details (name, tags, domains, notes) — more than the name/id that search or list_organizations surface. Use list_organizations to browse or search for a name-based lookup.",
|
|
2487
|
+
inputSchema: z.object({ organization_id: z.number().int().describe("Organization ID — the numeric id of the Zendesk organization to fetch. Obtain it from list_organizations, search, or a user record.") }),
|
|
2480
2488
|
annotations: {
|
|
2481
2489
|
readOnlyHint: true,
|
|
2482
2490
|
destructiveHint: false,
|