@crypto512/jicon-mcp 1.3.0 → 2.0.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.
Files changed (160) hide show
  1. package/README.md +68 -85
  2. package/TOOL_LIST.md +704 -87
  3. package/dist/config/constants.d.ts +18 -7
  4. package/dist/config/constants.d.ts.map +1 -1
  5. package/dist/config/constants.js +21 -8
  6. package/dist/config/constants.js.map +1 -1
  7. package/dist/config/loader.d.ts +11 -11
  8. package/dist/config/loader.d.ts.map +1 -1
  9. package/dist/config/loader.js +53 -93
  10. package/dist/config/loader.js.map +1 -1
  11. package/dist/config/types.d.ts +3 -6
  12. package/dist/config/types.d.ts.map +1 -1
  13. package/dist/config/types.js +2 -4
  14. package/dist/config/types.js.map +1 -1
  15. package/dist/confluence/formatters.js +1 -1
  16. package/dist/confluence/formatters.js.map +1 -1
  17. package/dist/confluence/tools.d.ts +4 -0
  18. package/dist/confluence/tools.d.ts.map +1 -1
  19. package/dist/confluence/tools.js +180 -125
  20. package/dist/confluence/tools.js.map +1 -1
  21. package/dist/index.js +17 -26
  22. package/dist/index.js.map +1 -1
  23. package/dist/jira/formatters.d.ts +1 -0
  24. package/dist/jira/formatters.d.ts.map +1 -1
  25. package/dist/jira/formatters.js +13 -12
  26. package/dist/jira/formatters.js.map +1 -1
  27. package/dist/jira/tools.d.ts +4 -0
  28. package/dist/jira/tools.d.ts.map +1 -1
  29. package/dist/jira/tools.js +189 -50
  30. package/dist/jira/tools.js.map +1 -1
  31. package/dist/permissions/tool-registry.d.ts +2 -2
  32. package/dist/permissions/tool-registry.d.ts.map +1 -1
  33. package/dist/permissions/tool-registry.js +4 -2
  34. package/dist/permissions/tool-registry.js.map +1 -1
  35. package/dist/permissions/write-home-validator.d.ts.map +1 -1
  36. package/dist/permissions/write-home-validator.js +13 -3
  37. package/dist/permissions/write-home-validator.js.map +1 -1
  38. package/dist/tempo/defaults.d.ts +17 -0
  39. package/dist/tempo/defaults.d.ts.map +1 -0
  40. package/dist/tempo/defaults.js +26 -0
  41. package/dist/tempo/defaults.js.map +1 -0
  42. package/dist/tempo/tools.d.ts +5 -0
  43. package/dist/tempo/tools.d.ts.map +1 -1
  44. package/dist/tempo/tools.js +108 -34
  45. package/dist/tempo/tools.js.map +1 -1
  46. package/dist/utils/buffer-pipeline/index.d.ts +30 -0
  47. package/dist/utils/buffer-pipeline/index.d.ts.map +1 -0
  48. package/dist/utils/buffer-pipeline/index.js +317 -0
  49. package/dist/utils/buffer-pipeline/index.js.map +1 -0
  50. package/dist/utils/buffer-pipeline/output/csv.d.ts +20 -0
  51. package/dist/utils/buffer-pipeline/output/csv.d.ts.map +1 -0
  52. package/dist/utils/buffer-pipeline/output/csv.js +117 -0
  53. package/dist/utils/buffer-pipeline/output/csv.js.map +1 -0
  54. package/dist/utils/buffer-pipeline/output/json.d.ts +16 -0
  55. package/dist/utils/buffer-pipeline/output/json.d.ts.map +1 -0
  56. package/dist/utils/buffer-pipeline/output/json.js +48 -0
  57. package/dist/utils/buffer-pipeline/output/json.js.map +1 -0
  58. package/dist/utils/buffer-pipeline/output/markdown.d.ts +15 -0
  59. package/dist/utils/buffer-pipeline/output/markdown.d.ts.map +1 -0
  60. package/dist/utils/buffer-pipeline/output/markdown.js +105 -0
  61. package/dist/utils/buffer-pipeline/output/markdown.js.map +1 -0
  62. package/dist/utils/buffer-pipeline/output/xhtml-list.d.ts +16 -0
  63. package/dist/utils/buffer-pipeline/output/xhtml-list.d.ts.map +1 -0
  64. package/dist/utils/buffer-pipeline/output/xhtml-list.js +81 -0
  65. package/dist/utils/buffer-pipeline/output/xhtml-list.js.map +1 -0
  66. package/dist/utils/buffer-pipeline/output/xhtml-table.d.ts +15 -0
  67. package/dist/utils/buffer-pipeline/output/xhtml-table.d.ts.map +1 -0
  68. package/dist/utils/buffer-pipeline/output/xhtml-table.js +176 -0
  69. package/dist/utils/buffer-pipeline/output/xhtml-table.js.map +1 -0
  70. package/dist/utils/buffer-pipeline/schema.d.ts +1878 -0
  71. package/dist/utils/buffer-pipeline/schema.d.ts.map +1 -0
  72. package/dist/utils/buffer-pipeline/schema.js +168 -0
  73. package/dist/utils/buffer-pipeline/schema.js.map +1 -0
  74. package/dist/utils/buffer-pipeline/stages/filter.d.ts +32 -0
  75. package/dist/utils/buffer-pipeline/stages/filter.d.ts.map +1 -0
  76. package/dist/utils/buffer-pipeline/stages/filter.js +208 -0
  77. package/dist/utils/buffer-pipeline/stages/filter.js.map +1 -0
  78. package/dist/utils/buffer-pipeline/stages/format.d.ts +45 -0
  79. package/dist/utils/buffer-pipeline/stages/format.d.ts.map +1 -0
  80. package/dist/utils/buffer-pipeline/stages/format.js +160 -0
  81. package/dist/utils/buffer-pipeline/stages/format.js.map +1 -0
  82. package/dist/utils/buffer-pipeline/stages/group-by.d.ts +25 -0
  83. package/dist/utils/buffer-pipeline/stages/group-by.d.ts.map +1 -0
  84. package/dist/utils/buffer-pipeline/stages/group-by.js +190 -0
  85. package/dist/utils/buffer-pipeline/stages/group-by.js.map +1 -0
  86. package/dist/utils/buffer-pipeline/stages/select.d.ts +54 -0
  87. package/dist/utils/buffer-pipeline/stages/select.d.ts.map +1 -0
  88. package/dist/utils/buffer-pipeline/stages/select.js +228 -0
  89. package/dist/utils/buffer-pipeline/stages/select.js.map +1 -0
  90. package/dist/utils/buffer-pipeline/stages/sort.d.ts +20 -0
  91. package/dist/utils/buffer-pipeline/stages/sort.d.ts.map +1 -0
  92. package/dist/utils/buffer-pipeline/stages/sort.js +96 -0
  93. package/dist/utils/buffer-pipeline/stages/sort.js.map +1 -0
  94. package/dist/utils/buffer-pipeline/types.d.ts +277 -0
  95. package/dist/utils/buffer-pipeline/types.d.ts.map +1 -0
  96. package/dist/utils/buffer-pipeline/types.js +8 -0
  97. package/dist/utils/buffer-pipeline/types.js.map +1 -0
  98. package/dist/utils/buffer-tools.d.ts +749 -19
  99. package/dist/utils/buffer-tools.d.ts.map +1 -1
  100. package/dist/utils/buffer-tools.js +738 -491
  101. package/dist/utils/buffer-tools.js.map +1 -1
  102. package/dist/utils/content-buffer.d.ts +55 -4
  103. package/dist/utils/content-buffer.d.ts.map +1 -1
  104. package/dist/utils/content-buffer.js +107 -9
  105. package/dist/utils/content-buffer.js.map +1 -1
  106. package/dist/utils/jicon-help.d.ts +1 -1
  107. package/dist/utils/jicon-help.d.ts.map +1 -1
  108. package/dist/utils/jicon-help.js +253 -28
  109. package/dist/utils/jicon-help.js.map +1 -1
  110. package/dist/utils/json-structure.d.ts +121 -0
  111. package/dist/utils/json-structure.d.ts.map +1 -0
  112. package/dist/utils/json-structure.js +637 -0
  113. package/dist/utils/json-structure.js.map +1 -0
  114. package/dist/utils/plantuml/include-expander.d.ts +31 -30
  115. package/dist/utils/plantuml/include-expander.d.ts.map +1 -1
  116. package/dist/utils/plantuml/include-expander.js +167 -133
  117. package/dist/utils/plantuml/include-expander.js.map +1 -1
  118. package/dist/utils/plantuml/index.d.ts +3 -3
  119. package/dist/utils/plantuml/index.d.ts.map +1 -1
  120. package/dist/utils/plantuml/index.js +4 -4
  121. package/dist/utils/plantuml/index.js.map +1 -1
  122. package/dist/utils/plantuml/service.d.ts +13 -24
  123. package/dist/utils/plantuml/service.d.ts.map +1 -1
  124. package/dist/utils/plantuml/service.js +49 -99
  125. package/dist/utils/plantuml/service.js.map +1 -1
  126. package/dist/utils/plantuml/tools.d.ts.map +1 -1
  127. package/dist/utils/plantuml/tools.js +33 -72
  128. package/dist/utils/plantuml/tools.js.map +1 -1
  129. package/dist/utils/plantuml/types.d.ts +1 -35
  130. package/dist/utils/plantuml/types.d.ts.map +1 -1
  131. package/dist/utils/plantuml/types.js +1 -11
  132. package/dist/utils/plantuml/types.js.map +1 -1
  133. package/dist/utils/plantuml/validation-helper.d.ts +1 -1
  134. package/dist/utils/plantuml/validation-helper.js +12 -12
  135. package/dist/utils/plantuml/validation-helper.js.map +1 -1
  136. package/dist/utils/response-formatter.d.ts +61 -6
  137. package/dist/utils/response-formatter.d.ts.map +1 -1
  138. package/dist/utils/response-formatter.js +174 -91
  139. package/dist/utils/response-formatter.js.map +1 -1
  140. package/dist/utils/url-tools.d.ts.map +1 -1
  141. package/dist/utils/url-tools.js +22 -0
  142. package/dist/utils/url-tools.js.map +1 -1
  143. package/dist/utils/xhtml/error-locator.js +2 -2
  144. package/dist/utils/xhtml/error-locator.js.map +1 -1
  145. package/dist/utils/xhtml/index.d.ts +1 -1
  146. package/dist/utils/xhtml/index.d.ts.map +1 -1
  147. package/dist/utils/xhtml/index.js +1 -1
  148. package/dist/utils/xhtml/index.js.map +1 -1
  149. package/dist/utils/xhtml/parser.d.ts +34 -5
  150. package/dist/utils/xhtml/parser.d.ts.map +1 -1
  151. package/dist/utils/xhtml/parser.js +66 -11
  152. package/dist/utils/xhtml/parser.js.map +1 -1
  153. package/dist/utils/xhtml/plantuml.d.ts.map +1 -1
  154. package/dist/utils/xhtml/plantuml.js +5 -3
  155. package/dist/utils/xhtml/plantuml.js.map +1 -1
  156. package/dist/utils/xhtml/serializer.d.ts.map +1 -1
  157. package/dist/utils/xhtml/serializer.js +12 -15
  158. package/dist/utils/xhtml/serializer.js.map +1 -1
  159. package/package.json +12 -4
  160. package/crypto512-jicon-mcp-1.3.0.tgz +0 -0
@@ -2,12 +2,12 @@
2
2
  * Confluence MCP Tools
3
3
  */
4
4
  import { z } from "zod";
5
- import { formatSuccess, formatSuccessBuffered, formatError, isApiError } from "../utils/response-formatter.js";
5
+ import { formatSuccess, formatSuccessJson, formatSuccessXhtml, formatError, isApiError } from "../utils/response-formatter.js";
6
6
  import { contentBuffer } from "../utils/content-buffer.js";
7
7
  import { formatPageMetadata } from "./formatters.js";
8
- import { validateXhtmlAsync, parseXhtml, parseStructure, serializeXhtml, enhanceXhtmlError } from "../utils/xhtml/index.js";
8
+ import { validateXhtmlAsync, parseXhtml, enhanceXhtmlError } from "../utils/xhtml/index.js";
9
9
  import { detectRawPlantUml, detectDiagramType } from "../utils/xhtml/plantuml.js";
10
- import { expandPlantUmlInXhtml, collapseExpandedIncludesInXhtml } from "../utils/plantuml/index.js";
10
+ import { convertPlantUmlIncludesInXhtml, IncludeConversionError } from "../utils/plantuml/index.js";
11
11
  import { parseUrl } from "../utils/url-tools.js";
12
12
  import { DEFAULT_PAGE_EXPAND } from "./defaults.js";
13
13
  /**
@@ -136,30 +136,20 @@ async function validateContentForWrite(content, bufferId) {
136
136
  }
137
137
  return null; // Valid content
138
138
  }
139
- /**
140
- * Store XHTML content in buffer with element IDs for structured editing.
141
- * Parses XHTML, assigns data-jicon-id attributes, and stores with structure.
142
- * Content is validated before Confluence writes, so parsing always succeeds.
143
- */
144
- function storeXhtmlWithStructure(content, metadata) {
145
- const parseResult = parseXhtml(content);
146
- const structureResult = parseStructure(parseResult.document);
147
- const contentWithIds = serializeXhtml(parseResult.document);
148
- const bufferId = contentBuffer.storeWithStructure(contentWithIds, structureResult.structure, structureResult.nextId, metadata);
149
- return { bufferId, structure: structureResult.structure };
150
- }
151
139
  export function createConfluenceTools(client) {
152
140
  return {
153
141
  confluence_search_content: {
154
- description: `Search Confluence content using CQL. Auto-fetches all results.
142
+ description: `Search Confluence content using CQL. Auto-fetches all results (up to 5000).
155
143
 
156
- Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.
144
+ Entry point for finding Confluence pages by content, title, or metadata.
157
145
 
158
- TIP: See help(topic="cql") for CQL syntax guide.
146
+ REQUIRES: Valid CQL query (see help(topic="cql") for syntax)
147
+ RETURNS: bufferId (JSON array), itemCount with page IDs and titles
148
+ NEXT: confluence_get_page (to load), buffer_pipeline (for reports), buffer_grep (to search results)
159
149
 
160
- Example: type=page AND space=DOCS AND text~"API" ORDER BY lastmodified DESC
150
+ Example: confluence_search_content(cql="type=page AND space=DOCS AND text~'API'")
161
151
 
162
- WARNING: Use text~ (not content~ or body~). Use space KEY (not name).`,
152
+ WARNING: Use text~ not content~. Use space KEY not name.`,
163
153
  inputSchema: z.object({
164
154
  cql: z.string().describe("CQL query string"),
165
155
  expand: z.array(z.string()).optional().describe("Additional data to expand"),
@@ -167,7 +157,7 @@ WARNING: Use text~ (not content~ or body~). Use space KEY (not name).`,
167
157
  handler: async (args) => {
168
158
  try {
169
159
  const result = await client.searchContentAll(args.cql, args.expand);
170
- return formatSuccessBuffered(result, {
160
+ return formatSuccessJson(result, {
171
161
  resourceType: "confluence_search",
172
162
  title: `CQL: ${args.cql.substring(0, 100)}${args.cql.length > 100 ? "..." : ""}`,
173
163
  });
@@ -239,18 +229,19 @@ Use buffer_edit(bufferId, after=ID, content/plantuml/fromBufferId) to add conten
239
229
  handler: async (args) => {
240
230
  try {
241
231
  const result = await client.getPage(args.pageId, args.expand);
242
- const rawContent = result.body?.storage?.value || "";
243
- // Collapse expanded includes back to !include directives
244
- const content = collapseExpandedIncludesInXhtml(rawContent);
232
+ const content = result.body?.storage?.value || "";
245
233
  // Store content with element IDs for structured editing
246
- const { bufferId, structure } = storeXhtmlWithStructure(content, {
234
+ const xhtmlResult = formatSuccessXhtml(content, {
247
235
  resourceType: "confluence_page",
248
236
  resourceId: String(result.id),
249
- contentType: "xhtml",
250
237
  version: result.version?.number,
251
238
  spaceKey: result.space?.key,
252
- title: result.title,
239
+ title: result.title || args.pageId,
253
240
  });
241
+ if ("errorResult" in xhtmlResult) {
242
+ return xhtmlResult.errorResult;
243
+ }
244
+ const { bufferId, structure } = xhtmlResult;
254
245
  return formatSuccess({
255
246
  ...formatPageMetadata(result),
256
247
  pageId: result.id,
@@ -288,18 +279,19 @@ Use buffer_edit(bufferId, after=ID, content/plantuml/fromBufferId) to add conten
288
279
  statusCode: 404,
289
280
  });
290
281
  }
291
- const rawContent = result.body?.storage?.value || "";
292
- // Collapse expanded includes back to !include directives
293
- const content = collapseExpandedIncludesInXhtml(rawContent);
282
+ const content = result.body?.storage?.value || "";
294
283
  // Store content with element IDs for structured editing
295
- const { bufferId, structure } = storeXhtmlWithStructure(content, {
284
+ const xhtmlResult = formatSuccessXhtml(content, {
296
285
  resourceType: "confluence_page",
297
286
  resourceId: String(result.id),
298
- contentType: "xhtml",
299
287
  version: result.version?.number,
300
288
  spaceKey: result.space?.key,
301
- title: result.title,
289
+ title: result.title || args.title,
302
290
  });
291
+ if ("errorResult" in xhtmlResult) {
292
+ return xhtmlResult.errorResult;
293
+ }
294
+ const { bufferId, structure } = xhtmlResult;
303
295
  return formatSuccess({
304
296
  ...formatPageMetadata(result),
305
297
  pageId: result.id,
@@ -316,27 +308,11 @@ Use buffer_edit(bufferId, after=ID, content/plantuml/fromBufferId) to add conten
316
308
  },
317
309
  },
318
310
  confluence_edit: {
319
- description: `Smart page/draft loader - auto-resolves URLs, pageIds, draftIds, or SPACE/Title.
311
+ description: `Load page/draft for editing. Returns bufferId, structure (element IDs), pageId, version.
320
312
 
321
- ACCEPTS ANY OF:
322
- - Full URL: https://confluence.example.com/pages/viewpage.action?pageId=123
323
- - Full URL: https://confluence.example.com/pages/resumedraft.action?draftId=456
324
- - Full URL: https://confluence.example.com/display/SPACE/Page+Title
325
- - Page ID: "123456"
326
- - Draft ID: "draft:123456" (prefix with "draft:")
327
- - Space/Title: "DOCS/API Guide"
328
-
329
- SMART BEHAVIOR:
330
- - URLs are parsed automatically to extract pageId or draftId
331
- - Draft IDs: tries to load draft; if 404 (published), finds page by title
332
- - Returns bufferId + structure + pageId for editing
333
-
334
- WORKFLOW:
335
- 1. confluence_edit(input) → bufferId, structure, pageId
336
- 2. buffer_edit(bufferId, ...) → modify content
337
- 3. confluence_draft_create(pageId=..., bufferId=...) → draft linked to original page
338
- 4. User publishes via Confluence UI (updates original page)
339
- 5. For more edits: confluence_edit(same URL or "SPACE/Title") → auto-resolves`,
313
+ ACCEPTS: URL | pageId | "draft:ID" | "SPACE/Title"
314
+ RETURNS: bufferId, structure, pageId, version
315
+ NEXT: buffer_edit confluence_draft_create → confluence_review_publish`,
340
316
  inputSchema: z.object({
341
317
  input: z.string().describe('URL, pageId, "draft:ID", or "SPACE/Title"'),
342
318
  }),
@@ -351,16 +327,18 @@ WORKFLOW:
351
327
  }
352
328
  // Helper to load page content and return formatted result
353
329
  const loadPageContent = async (pageResult) => {
354
- const rawContent = pageResult.body?.storage?.value || "";
355
- const content = collapseExpandedIncludesInXhtml(rawContent);
356
- const { bufferId, structure } = storeXhtmlWithStructure(content, {
330
+ const content = pageResult.body?.storage?.value || "";
331
+ const xhtmlResult = formatSuccessXhtml(content, {
357
332
  resourceType: "confluence_page",
358
333
  resourceId: String(pageResult.id),
359
- contentType: "xhtml",
360
334
  version: pageResult.version?.number,
361
335
  spaceKey: pageResult.space?.key,
362
336
  title: pageResult.title,
363
337
  });
338
+ if ("errorResult" in xhtmlResult) {
339
+ throw new Error("Failed to parse XHTML content");
340
+ }
341
+ const { bufferId, structure } = xhtmlResult;
364
342
  return {
365
343
  pageId: pageResult.id,
366
344
  spaceKey: pageResult.space?.key,
@@ -375,16 +353,18 @@ WORKFLOW:
375
353
  // Helper to load draft content
376
354
  const loadDraftContent = async (draftId) => {
377
355
  const result = await client.getDraft(draftId);
378
- const rawContent = result.body?.storage?.value || "";
379
- const content = collapseExpandedIncludesInXhtml(rawContent);
380
- const { bufferId, structure } = storeXhtmlWithStructure(content, {
381
- resourceType: "confluence_page",
356
+ const content = result.body?.storage?.value || "";
357
+ const xhtmlResult = formatSuccessXhtml(content, {
358
+ resourceType: "confluence_draft",
382
359
  resourceId: String(result.id),
383
- contentType: "xhtml",
384
360
  spaceKey: result.space?.key,
385
361
  title: result.title,
386
362
  isDraft: true,
387
363
  });
364
+ if ("errorResult" in xhtmlResult) {
365
+ throw new Error("Failed to parse XHTML content");
366
+ }
367
+ const { bufferId, structure } = xhtmlResult;
388
368
  return {
389
369
  draftId: result.id,
390
370
  spaceKey: result.space?.key,
@@ -508,7 +488,15 @@ WORKFLOW:
508
488
  // - New pages: confluence_draft_create → user publishes
509
489
  // - Existing pages: confluence_get_page → buffer_edit → confluence_draft_create → user publishes
510
490
  confluence_delete_page: {
511
- description: "Delete a Confluence page",
491
+ description: `Delete a Confluence page permanently.
492
+
493
+ Page is moved to trash. Use with caution.
494
+
495
+ REQUIRES: pageId
496
+ RETURNS: Success confirmation
497
+ NEXT: None
498
+
499
+ Example: confluence_delete_page(pageId="123456")`,
512
500
  inputSchema: z.object({
513
501
  pageId: z.coerce.string().describe("Page ID (accepts string or number)"),
514
502
  }),
@@ -533,7 +521,13 @@ WORKFLOW:
533
521
  confluence_list_spaces: {
534
522
  description: `List all accessible Confluence spaces.
535
523
 
536
- Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
524
+ Discover available spaces and their keys for use in CQL queries.
525
+
526
+ REQUIRES: None (optional type filter)
527
+ RETURNS: bufferId with spaces array (key, name, type)
528
+ NEXT: confluence_get_space (for details), confluence_search_content (to query space)
529
+
530
+ Example: confluence_list_spaces(type="global")`,
537
531
  inputSchema: z.object({
538
532
  type: z
539
533
  .enum(["global", "personal"])
@@ -543,7 +537,7 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
543
537
  handler: async (args) => {
544
538
  try {
545
539
  const result = await client.listSpaces(args.type);
546
- return formatSuccessBuffered(result, {
540
+ return formatSuccessJson(result, {
547
541
  resourceType: "confluence_spaces",
548
542
  title: args.type ? `${args.type} spaces` : "All Spaces",
549
543
  });
@@ -554,9 +548,15 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
554
548
  },
555
549
  },
556
550
  confluence_get_space: {
557
- description: `Get detailed information about a space.
551
+ description: `Get detailed information about a Confluence space.
558
552
 
559
- Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
553
+ View space configuration, homepage, and description.
554
+
555
+ REQUIRES: spaceKey (short code like 'DOCS')
556
+ RETURNS: bufferId with space details (name, homepage, description)
557
+ NEXT: confluence_search_content (to query space), confluence_get_page (homepage)
558
+
559
+ Example: confluence_get_space(spaceKey="DOCS")`,
560
560
  inputSchema: z.object({
561
561
  spaceKey: z.string().describe("Space key"),
562
562
  expand: z.array(z.string()).optional().describe("Additional data to expand"),
@@ -564,7 +564,7 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
564
564
  handler: async (args) => {
565
565
  try {
566
566
  const result = await client.getSpace(args.spaceKey, args.expand);
567
- return formatSuccessBuffered(result, {
567
+ return formatSuccessJson(result, {
568
568
  resourceType: "confluence_space",
569
569
  title: result.name || args.spaceKey,
570
570
  spaceKey: args.spaceKey,
@@ -586,7 +586,7 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
586
586
  handler: async (args) => {
587
587
  try {
588
588
  const result = await client.getPageChildren(args.pageId, args.expand);
589
- return formatSuccessBuffered(result, {
589
+ return formatSuccessJson(result, {
590
590
  resourceType: "confluence_page_children",
591
591
  title: `Page ${args.pageId} children`,
592
592
  pageId: args.pageId,
@@ -623,7 +623,7 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
623
623
  handler: async (args) => {
624
624
  try {
625
625
  const result = await client.getComments(args.pageId);
626
- return formatSuccessBuffered(result, {
626
+ return formatSuccessJson(result, {
627
627
  resourceType: "confluence_comments",
628
628
  title: `Page ${args.pageId} comments`,
629
629
  pageId: args.pageId,
@@ -661,7 +661,7 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
661
661
  handler: async (args) => {
662
662
  try {
663
663
  const result = await client.listAttachments(args.pageId);
664
- return formatSuccessBuffered(result, {
664
+ return formatSuccessJson(result, {
665
665
  resourceType: "confluence_attachments",
666
666
  title: `Page ${args.pageId} attachments`,
667
667
  pageId: args.pageId,
@@ -673,14 +673,8 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
673
673
  },
674
674
  },
675
675
  confluence_get_current_user_space: {
676
- description: `Get the current user's personal/home Confluence space.
677
-
678
- Use this tool when the user asks about:
679
- - "my space", "my home space", "my personal space"
680
- - "confluence home", "home space"
681
- - "where can I write", "my confluence area"
682
-
683
- Returns the user's personal space key and details. Use this to verify your personal space before write operations when write-home restriction is enabled.`,
676
+ description: `Get the current user's personal Confluence space. Returns spaceKey and homePageId.
677
+ Use to verify personal space before write operations when write-home restriction is enabled.`,
684
678
  inputSchema: z.object({}),
685
679
  handler: async () => {
686
680
  try {
@@ -712,30 +706,16 @@ Returns the user's personal space key and details. Use this to verify your perso
712
706
  // Note: Confluence drafts cannot be updated via API, only created, read, or deleted.
713
707
  // The workflow is: create draft → edit locally in buffer → save (delete+recreate) → publish
714
708
  confluence_draft_create: {
715
- description: `Create a Confluence draft for user review. Returns draftId, bufferId, structure (element IDs), and clickable URL.
709
+ description: `Create draft for user review. Requires bufferId with valid XHTML.
716
710
 
717
- REQUIRES bufferId - content must be in a buffer for validation and error recovery.
711
+ NEW page: confluence_draft_create(spaceKey, title, bufferId)
712
+ EDIT page: confluence_edit → buffer_edit → confluence_draft_create(pageId, bufferId)
718
713
 
719
- Workflow for NEW page:
720
- 1. buffer_create(content="<h1>Title</h1><p>Content</p>", contentType="xhtml") → bufferId, structure
721
- 2. buffer_validate_xhtml(bufferId) check for errors, get elementId if invalid
722
- 3. buffer_edit(bufferId, replace=elementId, content="...") → fix errors if any
723
- 4. confluence_draft_create(spaceKey, title, bufferId) → creates draft for review
724
-
725
- Workflow for EDITING existing page:
726
- 1. confluence_get_page(pageId) or confluence_edit(input) → bufferId, structure
727
- 2. buffer_edit(bufferId, after=ID, content/plantuml) → modify content
728
- 3. buffer_validate_xhtml(bufferId) → check for errors
729
- 4. confluence_draft_create(pageId, bufferId) → creates "[jicon-mcp REVIEW] Title" draft
730
- 5. User reviews in Confluence UI
731
- 6. confluence_review_publish(draftId) → applies changes to original page
732
-
733
- On validation error: returns bufferId + errorElementId for surgical fix with buffer_edit.
734
-
735
- IMPORTANT: Call help(topic="storage") for XHTML syntax (HTML vs XHTML differences).
736
- IMPORTANT: Call help(topic="plantuml") for diagram syntax.`,
714
+ Draft title auto-prefixed with "[jicon-mcp REVIEW]" when editing existing pages.
715
+ RETURNS: draftId, bufferId, structure, url. On XHTML error: errorElementId for buffer_edit fix.
716
+ WARNING: pageId must be a PAGE ID, not a draft ID. Use confluence_draft_open for existing drafts.`,
737
717
  inputSchema: z.object({
738
- pageId: z.coerce.string().optional().describe("Existing page ID to create edit draft for. When provided, bufferId must come from that page."),
718
+ pageId: z.coerce.string().optional().describe("Existing PAGE ID to edit (NOT a draft ID). Get from confluence_get_page or confluence_edit. When provided, bufferId must come from that page."),
739
719
  spaceKey: z.string().optional().describe("Space key (required for new pages, auto-populated when pageId is provided)"),
740
720
  title: z.string().optional().describe("Page title (required for new pages, auto-populated when pageId is provided)"),
741
721
  bufferId: z.string().describe("Buffer ID containing XHTML content (from buffer_create or confluence_get_page)"),
@@ -773,7 +753,7 @@ IMPORTANT: Call help(topic="plantuml") for diagram syntax.`,
773
753
  });
774
754
  }
775
755
  // Get full content from buffer
776
- const fullContent = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.totalSize);
756
+ const fullContent = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
777
757
  if (!fullContent) {
778
758
  return formatError({
779
759
  error: true,
@@ -817,10 +797,18 @@ IMPORTANT: Call help(topic="plantuml") for diagram syntax.`,
817
797
  }
818
798
  }
819
799
  catch (error) {
800
+ // Provide helpful hint if they might have passed a draftId instead of pageId
801
+ const errorMessage = isApiError(error) ? error.message :
802
+ error instanceof Error ? error.message : String(error);
820
803
  return formatError({
821
804
  error: true,
822
- message: `Failed to fetch original page ${args.pageId}: ${error instanceof Error ? error.message : String(error)}`,
805
+ message: `Page '${args.pageId}' not found. If this is a draft ID (from confluence_draft_create), use confluence_draft_open to continue editing instead.`,
823
806
  statusCode: 404,
807
+ details: {
808
+ originalError: errorMessage,
809
+ hint: "For existing drafts: confluence_draft_open(draftId) → buffer_edit → confluence_draft_save. For new drafts from existing pages: confluence_edit(pageId) → buffer_edit → confluence_draft_create(pageId, bufferId)",
810
+ pageIdProvided: args.pageId,
811
+ },
824
812
  });
825
813
  }
826
814
  }
@@ -852,8 +840,26 @@ IMPORTANT: Call help(topic="plantuml") for diagram syntax.`,
852
840
  if (validationError) {
853
841
  return validationError;
854
842
  }
855
- // Expand PlantUML !include directives before sending to Confluence
856
- const expandedContent = await expandPlantUmlInXhtml(content);
843
+ // Convert PlantUML !include URLs to stdlib syntax before sending to Confluence
844
+ let expandedContent;
845
+ try {
846
+ expandedContent = convertPlantUmlIncludesInXhtml(content);
847
+ }
848
+ catch (error) {
849
+ if (error instanceof IncludeConversionError) {
850
+ return formatError({
851
+ error: true,
852
+ message: error.message,
853
+ statusCode: 400,
854
+ details: {
855
+ url: error.url,
856
+ suggestion: error.suggestion,
857
+ hint: "Replace the HTTP URL with the suggested stdlib syntax in your PlantUML diagram",
858
+ },
859
+ });
860
+ }
861
+ throw error;
862
+ }
857
863
  try {
858
864
  // Constants for review workflow
859
865
  const REVIEW_PREFIX = '[jicon-mcp REVIEW] ';
@@ -915,16 +921,19 @@ IMPORTANT: Call help(topic="plantuml") for diagram syntax.`,
915
921
  }
916
922
  // Store content with element IDs for structured editing
917
923
  // Track originalPageId in metadata so we can link the draft to its source
918
- const { bufferId: newBufferId } = storeXhtmlWithStructure(content, {
919
- resourceType: "confluence_page",
924
+ const xhtmlResult = formatSuccessXhtml(content, {
925
+ resourceType: "confluence_draft",
920
926
  resourceId: result.id,
921
- contentType: "xhtml",
922
927
  spaceKey: result.space?.key,
923
928
  title: result.title,
924
929
  isDraft: true,
925
930
  originalPageId: originalPageId,
926
931
  originalPageVersion: originalPageVersion,
927
932
  });
933
+ if ("errorResult" in xhtmlResult) {
934
+ return xhtmlResult.errorResult;
935
+ }
936
+ const newBufferId = xhtmlResult.bufferId;
928
937
  // Build the draft edit URL (drafts use resumedraft.action, not the webui link)
929
938
  const baseUrl = client.getBaseUrl();
930
939
  const draftUrl = `${baseUrl}/pages/resumedraft.action?draftId=${result.id}`;
@@ -1000,18 +1009,19 @@ Use buffer_edit(bufferId, after=ID, content/plantuml/fromBufferId) to modify, th
1000
1009
  handler: async (args) => {
1001
1010
  try {
1002
1011
  const result = await client.getDraft(args.draftId);
1003
- const rawContent = result.body?.storage?.value || "";
1004
- // Collapse expanded includes back to !include directives
1005
- const content = collapseExpandedIncludesInXhtml(rawContent);
1012
+ const content = result.body?.storage?.value || "";
1006
1013
  // Store content with element IDs for structured editing
1007
- const { bufferId, structure } = storeXhtmlWithStructure(content, {
1008
- resourceType: "confluence_page",
1014
+ const xhtmlResult = formatSuccessXhtml(content, {
1015
+ resourceType: "confluence_draft",
1009
1016
  resourceId: result.id,
1010
- contentType: "xhtml",
1011
1017
  spaceKey: result.space?.key,
1012
1018
  title: result.title,
1013
1019
  isDraft: true,
1014
1020
  });
1021
+ if ("errorResult" in xhtmlResult) {
1022
+ return xhtmlResult.errorResult;
1023
+ }
1024
+ const { bufferId, structure } = xhtmlResult;
1015
1025
  // Build the draft edit URL
1016
1026
  const baseUrl = client.getBaseUrl();
1017
1027
  const draftUrl = `${baseUrl}/pages/resumedraft.action?draftId=${result.id}`;
@@ -1053,7 +1063,7 @@ Returns bufferId. Use buffer_get_chunk to read, buffer_grep to search.`,
1053
1063
  created: page.version?.when || "",
1054
1064
  url: `${baseUrl}/pages/resumedraft.action?draftId=${page.id}`,
1055
1065
  }));
1056
- return formatSuccessBuffered({
1066
+ return formatSuccessJson({
1057
1067
  drafts,
1058
1068
  total: result.totalSize,
1059
1069
  }, {
@@ -1099,7 +1109,7 @@ Returns new draftId, bufferId, structure (element IDs), and URL. Always use the
1099
1109
  });
1100
1110
  }
1101
1111
  // Get full content from buffer
1102
- const fullContent = contentBuffer.getChunk(args.bufferId, 0, bufferChunk.totalSize);
1112
+ const fullContent = contentBuffer.getChunk(args.bufferId, 0, bufferChunk.bufferSizeBytes);
1103
1113
  if (!fullContent) {
1104
1114
  return formatError({
1105
1115
  error: true,
@@ -1126,8 +1136,26 @@ Returns new draftId, bufferId, structure (element IDs), and URL. Always use the
1126
1136
  if (validationError) {
1127
1137
  return validationError;
1128
1138
  }
1129
- // Expand PlantUML !include directives before sending to Confluence
1130
- const expandedContent = await expandPlantUmlInXhtml(savedContent);
1139
+ // Convert PlantUML !include URLs to stdlib syntax before sending to Confluence
1140
+ let expandedContent;
1141
+ try {
1142
+ expandedContent = convertPlantUmlIncludesInXhtml(savedContent);
1143
+ }
1144
+ catch (error) {
1145
+ if (error instanceof IncludeConversionError) {
1146
+ return formatError({
1147
+ error: true,
1148
+ message: error.message,
1149
+ statusCode: 400,
1150
+ details: {
1151
+ url: error.url,
1152
+ suggestion: error.suggestion,
1153
+ hint: "Replace the HTTP URL with the suggested stdlib syntax in your PlantUML diagram",
1154
+ },
1155
+ });
1156
+ }
1157
+ throw error;
1158
+ }
1131
1159
  const title = args.title || existingDraft.title;
1132
1160
  // Delete old draft before creating new one
1133
1161
  // NOTE: Confluence Data Center does NOT support updating drafts directly.
@@ -1158,18 +1186,21 @@ Returns new draftId, bufferId, structure (element IDs), and URL. Always use the
1158
1186
  });
1159
1187
  // Update buffer metadata with new draft ID
1160
1188
  contentBuffer.invalidateByMetadata({
1161
- resourceType: "confluence_page",
1189
+ resourceType: "confluence_draft",
1162
1190
  resourceId: args.draftId,
1163
1191
  });
1164
1192
  // Store content with element IDs for structured editing
1165
- const { bufferId: newBufferId, structure } = storeXhtmlWithStructure(savedContent, {
1166
- resourceType: "confluence_page",
1193
+ const xhtmlResult = formatSuccessXhtml(savedContent, {
1194
+ resourceType: "confluence_draft",
1167
1195
  resourceId: newDraft.id,
1168
- contentType: "xhtml",
1169
1196
  spaceKey: newDraft.space?.key,
1170
1197
  title: newDraft.title,
1171
1198
  isDraft: true,
1172
1199
  });
1200
+ if ("errorResult" in xhtmlResult) {
1201
+ return xhtmlResult.errorResult;
1202
+ }
1203
+ const { bufferId: newBufferId, structure } = xhtmlResult;
1173
1204
  // Build the draft edit URL
1174
1205
  const baseUrl = client.getBaseUrl();
1175
1206
  const draftUrl = `${baseUrl}/pages/resumedraft.action?draftId=${newDraft.id}`;
@@ -1230,14 +1261,24 @@ Drafts are NOT sent to trash - they are permanently deleted.`,
1230
1261
  confluence_review_publish: {
1231
1262
  description: `Publish a review draft to apply changes to the original page.
1232
1263
 
1264
+ Apply changes from "[jicon-mcp REVIEW]" draft to the original page.
1265
+
1266
+ REQUIRES: reviewDraftId (from confluence_draft_create with pageId)
1267
+ RETURNS: Updated page info with new version number
1268
+ NEXT: confluence_get_page (to verify), confluence_edit (for more changes)
1269
+
1233
1270
  This tool:
1234
1271
  1. Validates the draft is a "[jicon-mcp REVIEW]" draft with proper label
1235
1272
  2. Copies the draft content to the original page (creates new version)
1236
1273
  3. Deletes the review draft
1237
1274
 
1238
- Use this after user has reviewed the draft in Confluence UI.`,
1275
+ Example: confluence_review_publish(reviewDraftId="123456")`,
1239
1276
  inputSchema: z.object({
1240
1277
  reviewDraftId: z.coerce.string().describe("ID of the [jicon-mcp REVIEW] draft to publish"),
1278
+ autoRetry: z
1279
+ .boolean()
1280
+ .optional()
1281
+ .describe("Auto-retry once on 409 conflict (refetches page version)"),
1241
1282
  }),
1242
1283
  handler: async (args) => {
1243
1284
  const REVIEW_PREFIX = '[jicon-mcp REVIEW] ';
@@ -1294,9 +1335,23 @@ Use this after user has reviewed the draft in Confluence UI.`,
1294
1335
  // 5. Get the review draft content
1295
1336
  const reviewContent = reviewDraft.body?.storage?.value || "";
1296
1337
  // 6. Update original page with review content
1297
- const updatedPage = await client.updatePage(originalPageId, originalPage.version?.number || 1, originalPage.title, // Keep original title (not the REVIEW prefix)
1298
- reviewContent, false // Not a minor edit
1299
- );
1338
+ let updatedPage;
1339
+ try {
1340
+ updatedPage = await client.updatePage(originalPageId, originalPage.version?.number || 1, originalPage.title, // Keep original title (not the REVIEW prefix)
1341
+ reviewContent, false // Not a minor edit
1342
+ );
1343
+ }
1344
+ catch (updateError) {
1345
+ // Handle version conflicts with optional auto-retry
1346
+ if (isApiError(updateError) && updateError.statusCode === 409 && args.autoRetry) {
1347
+ // Re-fetch page to get current version and retry
1348
+ const refreshedPage = await client.getPage(originalPageId, ["version"]);
1349
+ updatedPage = await client.updatePage(originalPageId, refreshedPage.version?.number || 1, originalPage.title, reviewContent, false);
1350
+ }
1351
+ else {
1352
+ throw updateError;
1353
+ }
1354
+ }
1300
1355
  // 7. Delete review draft
1301
1356
  await client.deleteDraft(args.reviewDraftId);
1302
1357
  // 8. Invalidate buffers