@crypto512/jicon-mcp 2.2.1 → 2.3.19
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/PROMPT.md +10 -28
- package/README.md +39 -6
- package/TOOL_LIST.md +542 -407
- package/dist/config/constants.d.ts +28 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +34 -0
- package/dist/config/constants.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +12 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +49 -6
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +18 -0
- package/dist/config/types.js.map +1 -1
- package/dist/confluence/client.d.ts.map +1 -1
- package/dist/confluence/client.js +7 -2
- package/dist/confluence/client.js.map +1 -1
- package/dist/confluence/formatters.d.ts +20 -0
- package/dist/confluence/formatters.d.ts.map +1 -1
- package/dist/confluence/formatters.js +74 -8
- package/dist/confluence/formatters.js.map +1 -1
- package/dist/confluence/tools.d.ts +16 -20
- package/dist/confluence/tools.d.ts.map +1 -1
- package/dist/confluence/tools.js +71 -68
- package/dist/confluence/tools.js.map +1 -1
- package/dist/credentials/extractor.d.ts +15 -5
- package/dist/credentials/extractor.d.ts.map +1 -1
- package/dist/credentials/extractor.js +99 -12
- package/dist/credentials/extractor.js.map +1 -1
- package/dist/credentials/index.d.ts +4 -3
- package/dist/credentials/index.d.ts.map +1 -1
- package/dist/credentials/index.js +3 -2
- package/dist/credentials/index.js.map +1 -1
- package/dist/credentials/types.d.ts +22 -0
- package/dist/credentials/types.d.ts.map +1 -1
- package/dist/credentials/types.js.map +1 -1
- package/dist/index.js +211 -176
- package/dist/index.js.map +1 -1
- package/dist/jira/activity-tools.d.ts +8 -15
- package/dist/jira/activity-tools.d.ts.map +1 -1
- package/dist/jira/activity-tools.js +131 -90
- package/dist/jira/activity-tools.js.map +1 -1
- package/dist/jira/client.d.ts +24 -0
- package/dist/jira/client.d.ts.map +1 -1
- package/dist/jira/client.js +65 -6
- package/dist/jira/client.js.map +1 -1
- package/dist/jira/formatters.d.ts +61 -0
- package/dist/jira/formatters.d.ts.map +1 -1
- package/dist/jira/formatters.js +83 -11
- package/dist/jira/formatters.js.map +1 -1
- package/dist/jira/tools.d.ts +78 -26
- package/dist/jira/tools.d.ts.map +1 -1
- package/dist/jira/tools.js +293 -130
- package/dist/jira/tools.js.map +1 -1
- package/dist/permissions/filter.d.ts.map +1 -1
- package/dist/permissions/filter.js +8 -4
- package/dist/permissions/filter.js.map +1 -1
- package/dist/permissions/tool-registry.d.ts +15 -13
- package/dist/permissions/tool-registry.d.ts.map +1 -1
- package/dist/permissions/tool-registry.js +19 -10
- package/dist/permissions/tool-registry.js.map +1 -1
- package/dist/session/context.d.ts +81 -0
- package/dist/session/context.d.ts.map +1 -0
- package/dist/session/context.js +107 -0
- package/dist/session/context.js.map +1 -0
- package/dist/session/index.d.ts +12 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +22 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +186 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +383 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/tempo/client.d.ts +14 -0
- package/dist/tempo/client.d.ts.map +1 -1
- package/dist/tempo/client.js +57 -0
- package/dist/tempo/client.js.map +1 -1
- package/dist/tempo/formatters.d.ts +13 -0
- package/dist/tempo/formatters.d.ts.map +1 -1
- package/dist/tempo/formatters.js +106 -20
- package/dist/tempo/formatters.js.map +1 -1
- package/dist/tempo/tools.d.ts +14 -13
- package/dist/tempo/tools.d.ts.map +1 -1
- package/dist/tempo/tools.js +203 -33
- package/dist/tempo/tools.js.map +1 -1
- package/dist/tempo/types.d.ts +20 -6
- package/dist/tempo/types.d.ts.map +1 -1
- package/dist/transport/http.d.ts +21 -5
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +193 -22
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/index.d.ts +7 -2
- package/dist/transport/index.d.ts.map +1 -1
- package/dist/transport/index.js +10 -4
- package/dist/transport/index.js.map +1 -1
- package/dist/utils/buffer-tools.d.ts +48 -724
- package/dist/utils/buffer-tools.d.ts.map +1 -1
- package/dist/utils/buffer-tools.js +337 -170
- package/dist/utils/buffer-tools.js.map +1 -1
- package/dist/utils/content-buffer.d.ts +10 -31
- package/dist/utils/content-buffer.d.ts.map +1 -1
- package/dist/utils/content-buffer.js +12 -86
- package/dist/utils/content-buffer.js.map +1 -1
- package/dist/utils/http-client.d.ts.map +1 -1
- package/dist/utils/http-client.js +99 -2
- package/dist/utils/http-client.js.map +1 -1
- package/dist/utils/jicon-help.d.ts +3 -3
- package/dist/utils/jicon-help.d.ts.map +1 -1
- package/dist/utils/jicon-help.js +164 -312
- package/dist/utils/jicon-help.js.map +1 -1
- package/dist/utils/logger.d.ts +43 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +102 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/plantuml/tools.d.ts.map +1 -1
- package/dist/utils/plantuml/tools.js +10 -9
- package/dist/utils/plantuml/tools.js.map +1 -1
- package/dist/utils/response-formatter.d.ts +20 -2
- package/dist/utils/response-formatter.d.ts.map +1 -1
- package/dist/utils/response-formatter.js +147 -17
- package/dist/utils/response-formatter.js.map +1 -1
- package/dist/utils/sandbox/formatters.d.ts +25 -0
- package/dist/utils/sandbox/formatters.d.ts.map +1 -0
- package/dist/utils/sandbox/formatters.js +690 -0
- package/dist/utils/sandbox/formatters.js.map +1 -0
- package/dist/utils/sandbox/helpers.d.ts +16 -0
- package/dist/utils/sandbox/helpers.d.ts.map +1 -0
- package/dist/utils/sandbox/helpers.js +252 -0
- package/dist/utils/sandbox/helpers.js.map +1 -0
- package/dist/utils/sandbox/index.d.ts +19 -0
- package/dist/utils/sandbox/index.d.ts.map +1 -0
- package/dist/utils/sandbox/index.js +269 -0
- package/dist/utils/sandbox/index.js.map +1 -0
- package/dist/utils/sandbox/schema.d.ts +55 -0
- package/dist/utils/sandbox/schema.d.ts.map +1 -0
- package/dist/utils/sandbox/schema.js +39 -0
- package/dist/utils/sandbox/schema.js.map +1 -0
- package/dist/utils/sandbox/types.d.ts +179 -0
- package/dist/utils/sandbox/types.d.ts.map +1 -0
- package/dist/utils/sandbox/types.js +8 -0
- package/dist/utils/sandbox/types.js.map +1 -0
- package/dist/utils/schemas/confluence.d.ts +41 -0
- package/dist/utils/schemas/confluence.d.ts.map +1 -0
- package/dist/utils/schemas/confluence.js +105 -0
- package/dist/utils/schemas/confluence.js.map +1 -0
- package/dist/utils/schemas/index.d.ts +77 -0
- package/dist/utils/schemas/index.d.ts.map +1 -0
- package/dist/utils/schemas/index.js +107 -0
- package/dist/utils/schemas/index.js.map +1 -0
- package/dist/utils/schemas/jira.d.ts +49 -0
- package/dist/utils/schemas/jira.d.ts.map +1 -0
- package/dist/utils/schemas/jira.js +153 -0
- package/dist/utils/schemas/jira.js.map +1 -0
- package/dist/utils/schemas/tempo.d.ts +29 -0
- package/dist/utils/schemas/tempo.d.ts.map +1 -0
- package/dist/utils/schemas/tempo.js +72 -0
- package/dist/utils/schemas/tempo.js.map +1 -0
- package/dist/utils/whoami-tools.d.ts +17 -0
- package/dist/utils/whoami-tools.d.ts.map +1 -0
- package/dist/utils/whoami-tools.js +90 -0
- package/dist/utils/whoami-tools.js.map +1 -0
- package/dist/utils/xhtml/error-locator.js +5 -5
- package/dist/utils/xhtml/error-locator.js.map +1 -1
- package/package.json +10 -9
- package/dist/credentials/client-factory.d.ts +0 -64
- package/dist/credentials/client-factory.d.ts.map +0 -1
- package/dist/credentials/client-factory.js +0 -110
- package/dist/credentials/client-factory.js.map +0 -1
- package/dist/credentials/context.d.ts +0 -25
- package/dist/credentials/context.d.ts.map +0 -1
- package/dist/credentials/context.js +0 -35
- package/dist/credentials/context.js.map +0 -1
- package/dist/utils/buffer-pipeline/index.d.ts +0 -30
- package/dist/utils/buffer-pipeline/index.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/index.js +0 -317
- package/dist/utils/buffer-pipeline/index.js.map +0 -1
- package/dist/utils/buffer-pipeline/output/csv.d.ts +0 -20
- package/dist/utils/buffer-pipeline/output/csv.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/output/csv.js +0 -117
- package/dist/utils/buffer-pipeline/output/csv.js.map +0 -1
- package/dist/utils/buffer-pipeline/output/json.d.ts +0 -16
- package/dist/utils/buffer-pipeline/output/json.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/output/json.js +0 -48
- package/dist/utils/buffer-pipeline/output/json.js.map +0 -1
- package/dist/utils/buffer-pipeline/output/markdown.d.ts +0 -15
- package/dist/utils/buffer-pipeline/output/markdown.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/output/markdown.js +0 -105
- package/dist/utils/buffer-pipeline/output/markdown.js.map +0 -1
- package/dist/utils/buffer-pipeline/output/xhtml-list.d.ts +0 -16
- package/dist/utils/buffer-pipeline/output/xhtml-list.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/output/xhtml-list.js +0 -81
- package/dist/utils/buffer-pipeline/output/xhtml-list.js.map +0 -1
- package/dist/utils/buffer-pipeline/output/xhtml-table.d.ts +0 -15
- package/dist/utils/buffer-pipeline/output/xhtml-table.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/output/xhtml-table.js +0 -176
- package/dist/utils/buffer-pipeline/output/xhtml-table.js.map +0 -1
- package/dist/utils/buffer-pipeline/schema.d.ts +0 -1878
- package/dist/utils/buffer-pipeline/schema.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/schema.js +0 -168
- package/dist/utils/buffer-pipeline/schema.js.map +0 -1
- package/dist/utils/buffer-pipeline/stages/filter.d.ts +0 -32
- package/dist/utils/buffer-pipeline/stages/filter.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/stages/filter.js +0 -208
- package/dist/utils/buffer-pipeline/stages/filter.js.map +0 -1
- package/dist/utils/buffer-pipeline/stages/format.d.ts +0 -45
- package/dist/utils/buffer-pipeline/stages/format.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/stages/format.js +0 -160
- package/dist/utils/buffer-pipeline/stages/format.js.map +0 -1
- package/dist/utils/buffer-pipeline/stages/group-by.d.ts +0 -25
- package/dist/utils/buffer-pipeline/stages/group-by.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/stages/group-by.js +0 -190
- package/dist/utils/buffer-pipeline/stages/group-by.js.map +0 -1
- package/dist/utils/buffer-pipeline/stages/select.d.ts +0 -54
- package/dist/utils/buffer-pipeline/stages/select.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/stages/select.js +0 -228
- package/dist/utils/buffer-pipeline/stages/select.js.map +0 -1
- package/dist/utils/buffer-pipeline/stages/sort.d.ts +0 -20
- package/dist/utils/buffer-pipeline/stages/sort.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/stages/sort.js +0 -96
- package/dist/utils/buffer-pipeline/stages/sort.js.map +0 -1
- package/dist/utils/buffer-pipeline/types.d.ts +0 -277
- package/dist/utils/buffer-pipeline/types.d.ts.map +0 -1
- package/dist/utils/buffer-pipeline/types.js +0 -8
- package/dist/utils/buffer-pipeline/types.js.map +0 -1
- package/dist/utils/plantuml/docker-manager.d.ts +0 -37
- package/dist/utils/plantuml/docker-manager.d.ts.map +0 -1
- package/dist/utils/plantuml/docker-manager.js +0 -284
- package/dist/utils/plantuml/docker-manager.js.map +0 -1
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides tools to retrieve chunks from buffered content,
|
|
5
5
|
* list active buffers, clear buffers, search content, and edit content.
|
|
6
|
+
*
|
|
7
|
+
* All buffer operations use the session-scoped buffer from getSessionBuffer()
|
|
8
|
+
* which provides isolation between MCP sessions.
|
|
6
9
|
*/
|
|
7
10
|
import { z } from "zod";
|
|
8
|
-
import {
|
|
11
|
+
import { getSessionBuffer } from "../session/context.js";
|
|
9
12
|
import { formatSuccess, formatSuccessDirect, formatError, getMaxOutputSize } from "./response-formatter.js";
|
|
10
|
-
import {
|
|
13
|
+
import { getSchema, extractSamples } from "./schemas/index.js";
|
|
14
|
+
import { executeTransform, BufferTransformInputSchema, } from "./sandbox/index.js";
|
|
11
15
|
import { createJsonStructure, replaceInJsonArray, setByKeys, appendToJsonArray } from "./json-structure.js";
|
|
12
16
|
import { parseXhtml, serializeXhtml, validateXhtmlAsync, buildPlantUmlMacro, detectRawPlantUml, detectPlantUmlInContent,
|
|
13
17
|
// Element ID-based operations
|
|
@@ -63,7 +67,7 @@ function formatGrepOutput(result, showLineNumbers) {
|
|
|
63
67
|
* Type-safe buffer access: tools should only accept buffers of the type they understand.
|
|
64
68
|
*/
|
|
65
69
|
function validateBufferType(bufferId, expectedTypes, toolName) {
|
|
66
|
-
const bufferInfo =
|
|
70
|
+
const bufferInfo = getSessionBuffer().getInfo(bufferId);
|
|
67
71
|
if (!bufferInfo) {
|
|
68
72
|
return {
|
|
69
73
|
error: true,
|
|
@@ -74,7 +78,7 @@ function validateBufferType(bufferId, expectedTypes, toolName) {
|
|
|
74
78
|
details: {
|
|
75
79
|
code: "BUFFER_NOT_FOUND",
|
|
76
80
|
bufferId,
|
|
77
|
-
hint:
|
|
81
|
+
hint: 'Use buffer_list() to see active buffers. Call help(topic="buffers") for buffer workflow guide.',
|
|
78
82
|
},
|
|
79
83
|
}),
|
|
80
84
|
};
|
|
@@ -82,11 +86,11 @@ function validateBufferType(bufferId, expectedTypes, toolName) {
|
|
|
82
86
|
const actualType = bufferInfo.metadata?.contentType;
|
|
83
87
|
if (!expectedTypes.includes(actualType ?? "")) {
|
|
84
88
|
const typeHints = {
|
|
85
|
-
json: "Use
|
|
89
|
+
json: "Use buffer_transform for JSON data, or buffer_create(contentType='json') to create JSON buffers.",
|
|
86
90
|
xhtml: "Use buffer_create(contentType='xhtml') to create XHTML buffers, or confluence_get_page/confluence_edit to load Confluence content.",
|
|
87
91
|
};
|
|
88
92
|
const suggestedTools = {
|
|
89
|
-
json: "
|
|
93
|
+
json: "buffer_transform, buffer_get_items, buffer_set_items",
|
|
90
94
|
xhtml: "buffer_edit, buffer_get_structure, buffer_get_element, confluence_draft_create",
|
|
91
95
|
};
|
|
92
96
|
const hint = actualType ? typeHints[actualType] || "" : "";
|
|
@@ -104,6 +108,7 @@ function validateBufferType(bufferId, expectedTypes, toolName) {
|
|
|
104
108
|
actualType: actualType || "unknown",
|
|
105
109
|
hint: hint || `Create a ${expectedTypes[0]} buffer first.`,
|
|
106
110
|
suggestedTools: suggested ? `For ${actualType} buffers, use: ${suggested}` : undefined,
|
|
111
|
+
tip: 'Call help(topic="buffers") for buffer types and workflow guide.',
|
|
107
112
|
},
|
|
108
113
|
}),
|
|
109
114
|
};
|
|
@@ -153,7 +158,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
|
|
|
153
158
|
const op = operations[opIndex];
|
|
154
159
|
if (op.fromBufferId) {
|
|
155
160
|
// Validate source buffer exists
|
|
156
|
-
const sourceInfo =
|
|
161
|
+
const sourceInfo = getSessionBuffer().getInfo(op.fromBufferId);
|
|
157
162
|
if (!sourceInfo) {
|
|
158
163
|
return formatError({
|
|
159
164
|
error: true,
|
|
@@ -162,7 +167,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
|
|
|
162
167
|
});
|
|
163
168
|
}
|
|
164
169
|
// Get source buffer content
|
|
165
|
-
const sourceChunk =
|
|
170
|
+
const sourceChunk = getSessionBuffer().getChunk(op.fromBufferId, 0, sourceInfo.bufferSizeBytes);
|
|
166
171
|
if (!sourceChunk) {
|
|
167
172
|
return formatError({
|
|
168
173
|
error: true,
|
|
@@ -183,7 +188,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
|
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
190
|
// Get buffer content
|
|
186
|
-
const chunk =
|
|
191
|
+
const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
187
192
|
if (!chunk) {
|
|
188
193
|
return formatError({
|
|
189
194
|
error: true,
|
|
@@ -366,7 +371,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
|
|
|
366
371
|
// Rebuild structure
|
|
367
372
|
const newStructure = parseStructure(parseResult.document);
|
|
368
373
|
// Update buffer
|
|
369
|
-
|
|
374
|
+
getSessionBuffer().update(args.bufferId, newContent, { contentType: "xhtml" }, newStructure.structure, newStructure.nextId);
|
|
370
375
|
return formatSuccessDirect({
|
|
371
376
|
bufferId: args.bufferId,
|
|
372
377
|
success: true,
|
|
@@ -385,7 +390,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
|
|
|
385
390
|
function handlePlainTextEdit(args) {
|
|
386
391
|
// Handle append for plain/json buffers
|
|
387
392
|
if (args.append && args.content) {
|
|
388
|
-
const result =
|
|
393
|
+
const result = getSessionBuffer().append(args.bufferId, args.content);
|
|
389
394
|
if (!result) {
|
|
390
395
|
return formatError({
|
|
391
396
|
error: true,
|
|
@@ -412,7 +417,7 @@ function handlePlainTextEdit(args) {
|
|
|
412
417
|
},
|
|
413
418
|
});
|
|
414
419
|
}
|
|
415
|
-
const result =
|
|
420
|
+
const result = getSessionBuffer().edit(args.bufferId, args.oldString, args.newString, args.replaceAll ?? false);
|
|
416
421
|
if (!result) {
|
|
417
422
|
return formatError({
|
|
418
423
|
error: true,
|
|
@@ -435,7 +440,7 @@ export function createBufferTools() {
|
|
|
435
440
|
buffer_create: {
|
|
436
441
|
description: `Create a new buffer with initial content. Returns bufferId and structure.
|
|
437
442
|
|
|
438
|
-
Types: xhtml (Confluence storage, returns element IDs), json (data, use buffer_get_items or
|
|
443
|
+
Types: xhtml (Confluence storage, returns element IDs), json (data, use buffer_get_items or buffer_transform).
|
|
439
444
|
JSON accepts string or native object/array (auto-stringified).
|
|
440
445
|
Run help(topic="storage") for XHTML syntax.`,
|
|
441
446
|
inputSchema: z.object({
|
|
@@ -511,7 +516,7 @@ Run help(topic="storage") for XHTML syntax.`,
|
|
|
511
516
|
nextId = structureResult.nextId;
|
|
512
517
|
// Serialize back (now with data-jicon-id attributes)
|
|
513
518
|
const contentWithIds = serializeXhtml(parseResult.document);
|
|
514
|
-
bufferId =
|
|
519
|
+
bufferId = getSessionBuffer().storeWithStructure(contentWithIds, structure, nextId, mergedMetadata);
|
|
515
520
|
}
|
|
516
521
|
else if (args.contentType === "json") {
|
|
517
522
|
// Parse JSON and create structure with item IDs (for arrays)
|
|
@@ -529,14 +534,14 @@ Run help(topic="storage") for XHTML syntax.`,
|
|
|
529
534
|
// Create JSON structure (handles arrays with IDs, objects with keys)
|
|
530
535
|
const { content: contentWithIds, structure: jsonStruct } = createJsonStructure(data);
|
|
531
536
|
jsonStructure = jsonStruct;
|
|
532
|
-
bufferId =
|
|
537
|
+
bufferId = getSessionBuffer().storeJsonWithStructure(contentWithIds, jsonStruct, mergedMetadata);
|
|
533
538
|
}
|
|
534
539
|
else {
|
|
535
540
|
// TypeScript exhaustiveness check - should never reach here
|
|
536
541
|
const _exhaustiveCheck = args.contentType;
|
|
537
542
|
throw new Error(`Unsupported content type: ${_exhaustiveCheck}`);
|
|
538
543
|
}
|
|
539
|
-
const info =
|
|
544
|
+
const info = getSessionBuffer().getInfo(bufferId);
|
|
540
545
|
// Build response based on content type
|
|
541
546
|
const isArray = jsonStructure?.rootType === "array";
|
|
542
547
|
const isObject = jsonStructure?.rootType === "object";
|
|
@@ -548,7 +553,7 @@ Run help(topic="storage") for XHTML syntax.`,
|
|
|
548
553
|
: undefined;
|
|
549
554
|
// Build hint for JSON buffers
|
|
550
555
|
const hint = args.contentType === "json" && isArray
|
|
551
|
-
? `Use
|
|
556
|
+
? `Use buffer_transform(inputs={data:'${bufferId}'}, code='...') to filter, transform, and output.`
|
|
552
557
|
: args.contentType === "json"
|
|
553
558
|
? `Use buffer_get_items(bufferId='${bufferId}', keys=[...]) to access values, or buffer_grep to search.`
|
|
554
559
|
: undefined;
|
|
@@ -557,7 +562,6 @@ Run help(topic="storage") for XHTML syntax.`,
|
|
|
557
562
|
bufferSizeBytes: info?.bufferSizeBytes ?? contentStr.length,
|
|
558
563
|
contentType: args.contentType,
|
|
559
564
|
createdAt: info ? new Date(info.createdAt).toISOString() : new Date().toISOString(),
|
|
560
|
-
expiresAt: info ? new Date(info.expiresAt).toISOString() : undefined,
|
|
561
565
|
metadata: mergedMetadata,
|
|
562
566
|
// XHTML structure
|
|
563
567
|
...(structure && { structure, nextId }),
|
|
@@ -573,7 +577,7 @@ Run help(topic="storage") for XHTML syntax.`,
|
|
|
573
577
|
...(hint && { hint }),
|
|
574
578
|
message: args.contentType === "xhtml"
|
|
575
579
|
? "Buffer created with element IDs. Use buffer_edit with after/before/replace to modify."
|
|
576
|
-
: "JSON buffer created. Use buffer_get_items for access or
|
|
580
|
+
: "JSON buffer created. Use buffer_get_items for access or buffer_transform for transformation.",
|
|
577
581
|
});
|
|
578
582
|
}
|
|
579
583
|
catch (error) {
|
|
@@ -614,7 +618,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
|
|
|
614
618
|
if (typeError)
|
|
615
619
|
return typeError.result;
|
|
616
620
|
// Get full buffer content
|
|
617
|
-
const bufferInfo =
|
|
621
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
618
622
|
if (!bufferInfo) {
|
|
619
623
|
return formatError({
|
|
620
624
|
error: true,
|
|
@@ -622,7 +626,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
|
|
|
622
626
|
statusCode: 404,
|
|
623
627
|
});
|
|
624
628
|
}
|
|
625
|
-
const chunk =
|
|
629
|
+
const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
626
630
|
if (!chunk) {
|
|
627
631
|
return formatError({
|
|
628
632
|
error: true,
|
|
@@ -653,7 +657,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
|
|
|
653
657
|
if (parsed.length > 0 && typeof parsed[0] === "object" && parsed[0] !== null) {
|
|
654
658
|
itemSchema = Object.keys(parsed[0]);
|
|
655
659
|
}
|
|
656
|
-
return
|
|
660
|
+
return formatSuccessDirect({
|
|
657
661
|
bufferId: args.bufferId,
|
|
658
662
|
type: "array",
|
|
659
663
|
items,
|
|
@@ -663,7 +667,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
|
|
|
663
667
|
hasMore,
|
|
664
668
|
nextStart: hasMore ? start + count : undefined,
|
|
665
669
|
itemSchema: itemSchema ? itemSchema.slice(0, 20) : undefined,
|
|
666
|
-
});
|
|
670
|
+
}, { truncateHint: "Too many items. Use smaller count parameter." });
|
|
667
671
|
}
|
|
668
672
|
// Handle objects (new behavior)
|
|
669
673
|
if (typeof parsed === "object" && parsed !== null) {
|
|
@@ -683,7 +687,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
|
|
|
683
687
|
missingKeys.push(key);
|
|
684
688
|
}
|
|
685
689
|
}
|
|
686
|
-
return
|
|
690
|
+
return formatSuccessDirect({
|
|
687
691
|
bufferId: args.bufferId,
|
|
688
692
|
type: "object",
|
|
689
693
|
data: result,
|
|
@@ -691,20 +695,20 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
|
|
|
691
695
|
foundKeys,
|
|
692
696
|
...(missingKeys.length > 0 && { missingKeys }),
|
|
693
697
|
availableKeys: allKeys,
|
|
694
|
-
});
|
|
698
|
+
}, { truncateHint: "Response truncated. Request fewer keys." });
|
|
695
699
|
}
|
|
696
700
|
else {
|
|
697
701
|
// Return all keys
|
|
698
|
-
return
|
|
702
|
+
return formatSuccessDirect({
|
|
699
703
|
bufferId: args.bufferId,
|
|
700
704
|
type: "object",
|
|
701
705
|
data,
|
|
702
706
|
keys: allKeys,
|
|
703
|
-
});
|
|
707
|
+
}, { truncateHint: "Response truncated. Use keys parameter to request specific fields." });
|
|
704
708
|
}
|
|
705
709
|
}
|
|
706
710
|
// Handle primitives (edge case)
|
|
707
|
-
return
|
|
711
|
+
return formatSuccessDirect({
|
|
708
712
|
bufferId: args.bufferId,
|
|
709
713
|
type: "primitive",
|
|
710
714
|
data: parsed,
|
|
@@ -749,7 +753,7 @@ Returns: Updated buffer info with changes summary.`,
|
|
|
749
753
|
if (typeError)
|
|
750
754
|
return typeError.result;
|
|
751
755
|
// Get buffer info
|
|
752
|
-
const bufferInfo =
|
|
756
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
753
757
|
if (!bufferInfo) {
|
|
754
758
|
return formatError({
|
|
755
759
|
error: true,
|
|
@@ -776,7 +780,7 @@ Returns: Updated buffer info with changes summary.`,
|
|
|
776
780
|
},
|
|
777
781
|
});
|
|
778
782
|
}
|
|
779
|
-
const chunk =
|
|
783
|
+
const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
780
784
|
if (!chunk) {
|
|
781
785
|
return formatError({
|
|
782
786
|
error: true,
|
|
@@ -801,7 +805,7 @@ Returns: Updated buffer info with changes summary.`,
|
|
|
801
805
|
});
|
|
802
806
|
}
|
|
803
807
|
// Update buffer with new content
|
|
804
|
-
const updated =
|
|
808
|
+
const updated = getSessionBuffer().updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
|
|
805
809
|
if (!updated) {
|
|
806
810
|
return formatError({
|
|
807
811
|
error: true,
|
|
@@ -846,7 +850,7 @@ Returns: Updated buffer info with changes summary.`,
|
|
|
846
850
|
});
|
|
847
851
|
}
|
|
848
852
|
// Update buffer with new content
|
|
849
|
-
const updated =
|
|
853
|
+
const updated = getSessionBuffer().updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
|
|
850
854
|
if (!updated) {
|
|
851
855
|
return formatError({
|
|
852
856
|
error: true,
|
|
@@ -890,7 +894,7 @@ Returns: Updated buffer info with changes summary.`,
|
|
|
890
894
|
});
|
|
891
895
|
}
|
|
892
896
|
// Update buffer with new content
|
|
893
|
-
const updated =
|
|
897
|
+
const updated = getSessionBuffer().updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
|
|
894
898
|
if (!updated) {
|
|
895
899
|
return formatError({
|
|
896
900
|
error: true,
|
|
@@ -920,18 +924,17 @@ Returns: Updated buffer info with changes summary.`,
|
|
|
920
924
|
},
|
|
921
925
|
},
|
|
922
926
|
buffer_list: {
|
|
923
|
-
description: `List all active buffers with metadata. Buffers
|
|
924
|
-
Max
|
|
927
|
+
description: `List all active buffers with metadata. Buffers are cleaned up when session ends.
|
|
928
|
+
Max buffers per session configurable via JICON_MAX_BUFFERS (default: 10); oldest unmodified evicted first when full.`,
|
|
925
929
|
inputSchema: z.object({}),
|
|
926
930
|
handler: async () => {
|
|
927
931
|
try {
|
|
928
|
-
const buffers =
|
|
932
|
+
const buffers = getSessionBuffer().list();
|
|
929
933
|
return formatSuccess({
|
|
930
934
|
count: buffers.length,
|
|
931
935
|
buffers: buffers.map((b) => ({
|
|
932
936
|
...b,
|
|
933
937
|
createdAt: new Date(b.createdAt).toISOString(),
|
|
934
|
-
expiresAt: new Date(b.expiresAt).toISOString(),
|
|
935
938
|
})),
|
|
936
939
|
});
|
|
937
940
|
}
|
|
@@ -940,6 +943,113 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
|
|
|
940
943
|
}
|
|
941
944
|
},
|
|
942
945
|
},
|
|
946
|
+
buffer_get_schema: {
|
|
947
|
+
description: `Get the schema for a JSON buffer. Returns field names, types, and sample values.
|
|
948
|
+
|
|
949
|
+
Use this to understand buffer structure before writing buffer_transform code.
|
|
950
|
+
The schema shows all available fields with their types and example values from the actual data.
|
|
951
|
+
|
|
952
|
+
Returns:
|
|
953
|
+
- schemaType: Named schema type (e.g., "jira_issue", "tempo_worklog")
|
|
954
|
+
- fields: Object with field definitions (type, required, description, sample)
|
|
955
|
+
- itemCount: Number of items (for arrays)
|
|
956
|
+
|
|
957
|
+
Example: buffer_get_schema(bufferId="buf_xxx")`,
|
|
958
|
+
inputSchema: z.object({
|
|
959
|
+
bufferId: z.string().describe("Buffer ID to get schema for"),
|
|
960
|
+
}),
|
|
961
|
+
handler: async (args) => {
|
|
962
|
+
try {
|
|
963
|
+
// Type-safe buffer access: buffer_get_schema requires JSON buffer
|
|
964
|
+
const typeError = validateBufferType(args.bufferId, ["json"], "buffer_get_schema");
|
|
965
|
+
if (typeError)
|
|
966
|
+
return typeError.result;
|
|
967
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
968
|
+
if (!bufferInfo) {
|
|
969
|
+
return formatError({
|
|
970
|
+
error: true,
|
|
971
|
+
message: `Buffer not found or expired: ${args.bufferId}`,
|
|
972
|
+
statusCode: 404,
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
const schemaType = bufferInfo.schemaType;
|
|
976
|
+
// If buffer has a schemaType, get the schema definition and samples
|
|
977
|
+
if (schemaType) {
|
|
978
|
+
const schemaDef = getSchema(schemaType);
|
|
979
|
+
if (schemaDef) {
|
|
980
|
+
// Get actual data to extract samples
|
|
981
|
+
const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
982
|
+
if (chunk) {
|
|
983
|
+
try {
|
|
984
|
+
const data = JSON.parse(chunk.chunk);
|
|
985
|
+
const schemaWithSamples = extractSamples(schemaType, data);
|
|
986
|
+
if (schemaWithSamples) {
|
|
987
|
+
return formatSuccessDirect({
|
|
988
|
+
bufferId: args.bufferId,
|
|
989
|
+
schemaType,
|
|
990
|
+
description: schemaDef.description,
|
|
991
|
+
itemCount: bufferInfo.jsonStructure?.itemCount,
|
|
992
|
+
legend: {
|
|
993
|
+
required: "Always present with a non-null value",
|
|
994
|
+
optional: "May be null when not set — use `== null` to check",
|
|
995
|
+
searchable: "Can be used in JQL/CQL queries",
|
|
996
|
+
writable: "Can be set via update API",
|
|
997
|
+
jqlName: "JQL clause name when different from field name (e.g., type → issuetype)",
|
|
998
|
+
},
|
|
999
|
+
fields: schemaWithSamples.fields,
|
|
1000
|
+
}, { truncateHint: "Schema truncated." });
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
// Fall through to return schema without samples
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
// Return schema without samples if we couldn't extract them
|
|
1008
|
+
return formatSuccessDirect({
|
|
1009
|
+
bufferId: args.bufferId,
|
|
1010
|
+
schemaType,
|
|
1011
|
+
description: schemaDef.description,
|
|
1012
|
+
itemCount: bufferInfo.jsonStructure?.itemCount,
|
|
1013
|
+
legend: {
|
|
1014
|
+
required: "Always present with a non-null value",
|
|
1015
|
+
optional: "May be null when not set — use `== null` to check",
|
|
1016
|
+
searchable: "Can be used in JQL/CQL queries",
|
|
1017
|
+
writable: "Can be set via update API",
|
|
1018
|
+
jqlName: "JQL clause name when different from field name (e.g., type → issuetype)",
|
|
1019
|
+
},
|
|
1020
|
+
fields: schemaDef.fields,
|
|
1021
|
+
}, { truncateHint: "Schema truncated." });
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
// No schemaType - return structure info from buffer
|
|
1025
|
+
const jsonStructure = bufferInfo.jsonStructure;
|
|
1026
|
+
if (!jsonStructure) {
|
|
1027
|
+
return formatError({
|
|
1028
|
+
error: true,
|
|
1029
|
+
message: "Buffer does not have JSON structure",
|
|
1030
|
+
statusCode: 400,
|
|
1031
|
+
details: {
|
|
1032
|
+
hint: "This buffer may not be a JSON buffer or was created without structure tracking",
|
|
1033
|
+
},
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
// For untyped buffers, return what we know
|
|
1037
|
+
return formatSuccessDirect({
|
|
1038
|
+
bufferId: args.bufferId,
|
|
1039
|
+
schemaType: null,
|
|
1040
|
+
note: "Buffer has no schema type. Use buffer_get_items to inspect data structure.",
|
|
1041
|
+
structure: {
|
|
1042
|
+
type: jsonStructure.rootType,
|
|
1043
|
+
itemCount: jsonStructure.itemCount,
|
|
1044
|
+
keys: jsonStructure.keys,
|
|
1045
|
+
},
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
catch (error) {
|
|
1049
|
+
return formatError(error instanceof Error ? error : new Error(String(error)));
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
943
1053
|
buffer_clear: {
|
|
944
1054
|
description: `Clear a specific buffer or all buffers. Omit bufferId to clear all.`,
|
|
945
1055
|
inputSchema: z.object({
|
|
@@ -951,7 +1061,7 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
|
|
|
951
1061
|
handler: async (args) => {
|
|
952
1062
|
try {
|
|
953
1063
|
if (args.bufferId) {
|
|
954
|
-
const deleted =
|
|
1064
|
+
const deleted = getSessionBuffer().clear(args.bufferId);
|
|
955
1065
|
if (!deleted) {
|
|
956
1066
|
return formatError({
|
|
957
1067
|
error: true,
|
|
@@ -964,8 +1074,8 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
|
|
|
964
1074
|
cleared: 1,
|
|
965
1075
|
});
|
|
966
1076
|
}
|
|
967
|
-
const count =
|
|
968
|
-
|
|
1077
|
+
const count = getSessionBuffer().size();
|
|
1078
|
+
getSessionBuffer().clear();
|
|
969
1079
|
return formatSuccess({
|
|
970
1080
|
message: "All buffers cleared",
|
|
971
1081
|
cleared: count,
|
|
@@ -1027,7 +1137,7 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
|
|
|
1027
1137
|
// Handle -C overriding -A/-B
|
|
1028
1138
|
const before = args["-C"] ?? args["-B"] ?? 0;
|
|
1029
1139
|
const after = args["-C"] ?? args["-A"] ?? 0;
|
|
1030
|
-
const result =
|
|
1140
|
+
const result = getSessionBuffer().grep(args.bufferId, args.pattern, {
|
|
1031
1141
|
caseInsensitive: args["-i"] ?? false,
|
|
1032
1142
|
before,
|
|
1033
1143
|
after,
|
|
@@ -1067,7 +1177,7 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
|
|
|
1067
1177
|
if (fullText.length > maxSize) {
|
|
1068
1178
|
const wrapperData = { content: fullText, type: "grep_result" };
|
|
1069
1179
|
const { content: jsonContent, structure } = createJsonStructure(wrapperData);
|
|
1070
|
-
const newBufferId =
|
|
1180
|
+
const newBufferId = getSessionBuffer().storeJsonWithStructure(jsonContent, structure, {
|
|
1071
1181
|
contentType: "json",
|
|
1072
1182
|
type: "grep_result",
|
|
1073
1183
|
sourceBufferId: args.bufferId,
|
|
@@ -1144,7 +1254,7 @@ Run help(topic="buffers") for editing guide and examples.`,
|
|
|
1144
1254
|
}),
|
|
1145
1255
|
handler: async (args) => {
|
|
1146
1256
|
try {
|
|
1147
|
-
const bufferInfo =
|
|
1257
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
1148
1258
|
if (!bufferInfo) {
|
|
1149
1259
|
return formatError({
|
|
1150
1260
|
error: true,
|
|
@@ -1181,12 +1291,12 @@ Each element has:
|
|
|
1181
1291
|
const typeError = validateBufferType(args.bufferId, ["xhtml"], "buffer_get_structure");
|
|
1182
1292
|
if (typeError)
|
|
1183
1293
|
return typeError.result;
|
|
1184
|
-
const bufferInfo =
|
|
1185
|
-
return
|
|
1294
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
1295
|
+
return formatSuccessDirect({
|
|
1186
1296
|
bufferId: args.bufferId,
|
|
1187
1297
|
structure: bufferInfo?.structure ?? [],
|
|
1188
1298
|
nextId: bufferInfo?.nextId ?? 1,
|
|
1189
|
-
});
|
|
1299
|
+
}, { truncateHint: "Structure truncated. Document has many elements." });
|
|
1190
1300
|
}
|
|
1191
1301
|
catch (error) {
|
|
1192
1302
|
return formatError(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1211,7 +1321,7 @@ Example: After a Confluence error at element 12, use this to see the content:
|
|
|
1211
1321
|
const typeError = validateBufferType(args.bufferId, ["xhtml"], "buffer_get_element");
|
|
1212
1322
|
if (typeError)
|
|
1213
1323
|
return typeError.result;
|
|
1214
|
-
const bufferInfo =
|
|
1324
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
1215
1325
|
if (!bufferInfo) {
|
|
1216
1326
|
return formatError({
|
|
1217
1327
|
error: true,
|
|
@@ -1220,7 +1330,7 @@ Example: After a Confluence error at element 12, use this to see the content:
|
|
|
1220
1330
|
});
|
|
1221
1331
|
}
|
|
1222
1332
|
// Get full buffer content
|
|
1223
|
-
const chunk =
|
|
1333
|
+
const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
1224
1334
|
if (!chunk) {
|
|
1225
1335
|
return formatError({
|
|
1226
1336
|
error: true,
|
|
@@ -1263,13 +1373,13 @@ Example: After a Confluence error at element 12, use this to see the content:
|
|
|
1263
1373
|
if (elementType === "ac:structured-macro") {
|
|
1264
1374
|
elementType = element.getAttribute("ac:name") || "macro";
|
|
1265
1375
|
}
|
|
1266
|
-
return
|
|
1376
|
+
return formatSuccessDirect({
|
|
1267
1377
|
bufferId: args.bufferId,
|
|
1268
1378
|
elementId: args.elementId,
|
|
1269
1379
|
elementType,
|
|
1270
1380
|
lineCount,
|
|
1271
1381
|
content,
|
|
1272
|
-
});
|
|
1382
|
+
}, { truncateHint: "Element content truncated. Element is very large." });
|
|
1273
1383
|
}
|
|
1274
1384
|
catch (error) {
|
|
1275
1385
|
return formatError(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -1294,7 +1404,7 @@ Use before confluence_draft_create to catch errors early.`,
|
|
|
1294
1404
|
if (typeError)
|
|
1295
1405
|
return typeError.result;
|
|
1296
1406
|
// Get buffer content
|
|
1297
|
-
const bufferInfo =
|
|
1407
|
+
const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
|
|
1298
1408
|
if (!bufferInfo) {
|
|
1299
1409
|
return formatError({
|
|
1300
1410
|
error: true,
|
|
@@ -1302,7 +1412,7 @@ Use before confluence_draft_create to catch errors early.`,
|
|
|
1302
1412
|
statusCode: 404,
|
|
1303
1413
|
});
|
|
1304
1414
|
}
|
|
1305
|
-
const chunk =
|
|
1415
|
+
const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
1306
1416
|
if (!chunk) {
|
|
1307
1417
|
return formatError({
|
|
1308
1418
|
error: true,
|
|
@@ -1352,147 +1462,172 @@ Use before confluence_draft_create to catch errors early.`,
|
|
|
1352
1462
|
},
|
|
1353
1463
|
},
|
|
1354
1464
|
// =========================================================================
|
|
1355
|
-
// Buffer
|
|
1465
|
+
// Buffer Transform - JavaScript sandbox for data transformation
|
|
1356
1466
|
// =========================================================================
|
|
1357
|
-
|
|
1358
|
-
description: `
|
|
1467
|
+
buffer_transform: {
|
|
1468
|
+
description: `Execute JavaScript code in a secure sandbox to transform buffer data.
|
|
1469
|
+
|
|
1470
|
+
IMPORTANT: Call buffer_get_schema(bufferId) BEFORE writing transform code to discover field names, types, and which fields are optional (nullable).
|
|
1359
1471
|
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1472
|
+
FLAT FIELD ACCESS:
|
|
1473
|
+
Buffer data uses FLAT fields (not nested). Use the field names from the schema.
|
|
1474
|
+
CORRECT: i.status, i.assignee, i.priority
|
|
1475
|
+
WRONG: i.status.name, i.fields.status.name, i.assignee?.displayName
|
|
1364
1476
|
|
|
1365
|
-
|
|
1477
|
+
NULL HANDLING: Optional fields (priority, assignee, time fields) are null when not set.
|
|
1478
|
+
Filter unset: i.timeoriginalestimate == null (catches both null and undefined)
|
|
1479
|
+
Filter zero: i.timeoriginalestimate === 0 (only explicit zero)
|
|
1366
1480
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1481
|
+
INPUTS: Map of variable names to buffer IDs. Buffers are loaded as globals.
|
|
1482
|
+
CODE: ES2020 JavaScript (synchronous). Return value becomes output buffer(s).
|
|
1483
|
+
|
|
1484
|
+
OUTPUT FORMAT:
|
|
1485
|
+
- For CONFLUENCE → toTable(), toList() → XHTML
|
|
1486
|
+
- For DISPLAY → toMarkdownTable(), toMarkdownList() → Markdown
|
|
1487
|
+
|
|
1488
|
+
HELPERS: groupBy, sum, avg, min, max, count, unique, get, pick, omit, parseDate, formatDate, daysBetween
|
|
1489
|
+
|
|
1490
|
+
Example:
|
|
1491
|
+
inputs: { issues: "buf_xxx" }
|
|
1492
|
+
code: \`
|
|
1493
|
+
const open = issues.filter(i => i.status !== 'Done');
|
|
1494
|
+
return toMarkdownTable(open, {
|
|
1495
|
+
columns: [
|
|
1496
|
+
{ field: 'key', header: 'Issue', link: { type: 'jira' } },
|
|
1497
|
+
{ field: 'summary', header: 'Summary' },
|
|
1498
|
+
{ field: 'status', header: 'Status' },
|
|
1499
|
+
{ field: 'assignee', header: 'Assignee' }
|
|
1500
|
+
]
|
|
1501
|
+
});
|
|
1502
|
+
\``,
|
|
1503
|
+
inputSchema: BufferTransformInputSchema,
|
|
1370
1504
|
handler: async (args) => {
|
|
1371
1505
|
try {
|
|
1372
|
-
//
|
|
1373
|
-
const
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
if (!bufferInfo) {
|
|
1379
|
-
return formatError({
|
|
1380
|
-
error: true,
|
|
1381
|
-
message: `Buffer not found or expired: ${args.sourceBufferId}`,
|
|
1382
|
-
statusCode: 404,
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
// 2. Get buffer content
|
|
1386
|
-
const chunk = contentBuffer.getChunk(args.sourceBufferId, 0, bufferInfo.bufferSizeBytes);
|
|
1387
|
-
if (!chunk) {
|
|
1388
|
-
return formatError({
|
|
1389
|
-
error: true,
|
|
1390
|
-
message: `Failed to read buffer content: ${args.sourceBufferId}`,
|
|
1391
|
-
statusCode: 500,
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
// 3. Parse JSON array
|
|
1395
|
-
let data;
|
|
1396
|
-
try {
|
|
1397
|
-
const parsed = JSON.parse(chunk.chunk);
|
|
1398
|
-
if (!Array.isArray(parsed)) {
|
|
1506
|
+
// 1. Load all input buffers
|
|
1507
|
+
const inputData = {};
|
|
1508
|
+
for (const [name, bufferId] of Object.entries(args.inputs)) {
|
|
1509
|
+
// Type-safe buffer access: buffer_transform requires JSON buffers
|
|
1510
|
+
const typeError = validateBufferType(bufferId, ["json"], "buffer_transform");
|
|
1511
|
+
if (typeError) {
|
|
1399
1512
|
return formatError({
|
|
1400
1513
|
error: true,
|
|
1401
|
-
message: "
|
|
1514
|
+
message: `Input "${name}": ${typeError.result.content[0].type === "text" ? JSON.parse(typeError.result.content[0].text).message : "Invalid buffer"}`,
|
|
1402
1515
|
statusCode: 400,
|
|
1403
|
-
details: {
|
|
1516
|
+
details: { inputName: name, bufferId },
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
const bufferInfo = getSessionBuffer().getInfo(bufferId);
|
|
1520
|
+
if (!bufferInfo) {
|
|
1521
|
+
return formatError({
|
|
1522
|
+
error: true,
|
|
1523
|
+
message: `Input "${name}": Buffer not found or expired: ${bufferId}`,
|
|
1524
|
+
statusCode: 404,
|
|
1525
|
+
details: { inputName: name, bufferId },
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
const chunk = getSessionBuffer().getChunk(bufferId, 0, bufferInfo.bufferSizeBytes);
|
|
1529
|
+
if (!chunk) {
|
|
1530
|
+
return formatError({
|
|
1531
|
+
error: true,
|
|
1532
|
+
message: `Input "${name}": Failed to read buffer: ${bufferId}`,
|
|
1533
|
+
statusCode: 500,
|
|
1534
|
+
details: { inputName: name, bufferId },
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
try {
|
|
1538
|
+
inputData[name] = JSON.parse(chunk.chunk);
|
|
1539
|
+
}
|
|
1540
|
+
catch (e) {
|
|
1541
|
+
return formatError({
|
|
1542
|
+
error: true,
|
|
1543
|
+
message: `Input "${name}": Failed to parse as JSON: ${e.message}`,
|
|
1544
|
+
statusCode: 400,
|
|
1545
|
+
details: { inputName: name, bufferId },
|
|
1404
1546
|
});
|
|
1405
1547
|
}
|
|
1406
|
-
data = parsed;
|
|
1407
|
-
}
|
|
1408
|
-
catch (e) {
|
|
1409
|
-
return formatError({
|
|
1410
|
-
error: true,
|
|
1411
|
-
message: `Failed to parse buffer as JSON: ${e.message}`,
|
|
1412
|
-
statusCode: 400,
|
|
1413
|
-
});
|
|
1414
1548
|
}
|
|
1415
|
-
//
|
|
1549
|
+
// 2. Build context
|
|
1416
1550
|
const context = {
|
|
1417
|
-
sourceBufferId: args.sourceBufferId,
|
|
1418
1551
|
jiraUrl: process.env.JIRA_URL,
|
|
1419
1552
|
confluenceUrl: process.env.CONFLUENCE_URL,
|
|
1420
|
-
metadata: bufferInfo.metadata,
|
|
1421
1553
|
};
|
|
1422
|
-
//
|
|
1423
|
-
|
|
1424
|
-
|
|
1554
|
+
// 3. Execute in sandbox
|
|
1555
|
+
const result = await executeTransform({ inputs: args.inputs, code: args.code, limits: args.limits }, context, inputData);
|
|
1556
|
+
// 4. Handle errors
|
|
1557
|
+
if ("error" in result && result.error) {
|
|
1558
|
+
return formatError({
|
|
1559
|
+
error: true,
|
|
1560
|
+
message: result.message,
|
|
1561
|
+
statusCode: result.code === "TIMEOUT" || result.code === "MEMORY_LIMIT" ? 408 : 400,
|
|
1562
|
+
details: {
|
|
1563
|
+
code: result.code,
|
|
1564
|
+
...result.details,
|
|
1565
|
+
tip: 'Call help(topic="buffers") for transform syntax and helper functions.',
|
|
1566
|
+
},
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
// 5. Store output(s) as buffer(s)
|
|
1570
|
+
const transformResult = result;
|
|
1571
|
+
// Handle multiple outputs
|
|
1572
|
+
if (transformResult.multipleOutputs) {
|
|
1573
|
+
const buffers = {};
|
|
1574
|
+
for (const [outputName, output] of Object.entries(transformResult.multipleOutputs)) {
|
|
1575
|
+
const bufferId = await storeTransformOutput(output.contentType, output.content, { outputName });
|
|
1576
|
+
buffers[outputName] = bufferId;
|
|
1577
|
+
}
|
|
1425
1578
|
return formatSuccessDirect({
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
warnings: validation.warnings,
|
|
1430
|
-
availableFields: validation.availableFields,
|
|
1431
|
-
itemCount: data.length,
|
|
1579
|
+
buffers,
|
|
1580
|
+
outputCount: Object.keys(buffers).length,
|
|
1581
|
+
hint: "Multiple buffers created. Use buffer_get_items(bufferId) to access each.",
|
|
1432
1582
|
});
|
|
1433
1583
|
}
|
|
1434
|
-
//
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
return
|
|
1584
|
+
// Handle single XHTML output
|
|
1585
|
+
if (transformResult.xhtmlContent) {
|
|
1586
|
+
const bufferId = await storeTransformOutput("xhtml", transformResult.xhtmlContent, {});
|
|
1587
|
+
return formatSuccessDirect({
|
|
1588
|
+
bufferId,
|
|
1589
|
+
contentType: "xhtml",
|
|
1590
|
+
hint: "Use confluence_draft_create(bufferId) to publish, or buffer_edit to modify.",
|
|
1591
|
+
});
|
|
1438
1592
|
}
|
|
1439
|
-
//
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
outputType,
|
|
1452
|
-
};
|
|
1453
|
-
let newBufferId;
|
|
1454
|
-
if (contentType === "xhtml") {
|
|
1455
|
-
// Parse XHTML to get structure with element IDs
|
|
1456
|
-
const parseResult = parseXhtml(output);
|
|
1457
|
-
if (parseResult.document) {
|
|
1458
|
-
const structureResult = parseStructure(parseResult.document);
|
|
1459
|
-
// Re-serialize to include data-jicon-id attributes
|
|
1460
|
-
const contentWithIds = serializeXhtml(parseResult.document);
|
|
1461
|
-
newBufferId = contentBuffer.storeWithStructure(contentWithIds, structureResult.structure, structureResult.nextId, bufferMetadata);
|
|
1462
|
-
}
|
|
1463
|
-
else {
|
|
1464
|
-
// Fallback: store without structure if parsing fails
|
|
1465
|
-
newBufferId = contentBuffer.store(output, bufferMetadata);
|
|
1593
|
+
// Handle single Markdown output - return content directly (displayable)
|
|
1594
|
+
if (transformResult.markdownContent) {
|
|
1595
|
+
const markdownContent = transformResult.markdownContent;
|
|
1596
|
+
const bufferId = await storeTransformOutput("markdown", markdownContent, {});
|
|
1597
|
+
const maxContentSize = Math.floor(getMaxOutputSize() * 0.9);
|
|
1598
|
+
if (markdownContent.length <= maxContentSize) {
|
|
1599
|
+
// Return content directly - no extra tool call needed
|
|
1600
|
+
return formatSuccessDirect({
|
|
1601
|
+
bufferId,
|
|
1602
|
+
format: "markdown",
|
|
1603
|
+
content: markdownContent,
|
|
1604
|
+
});
|
|
1466
1605
|
}
|
|
1606
|
+
// Truncate large content
|
|
1607
|
+
return formatSuccessDirect({
|
|
1608
|
+
bufferId,
|
|
1609
|
+
format: "markdown",
|
|
1610
|
+
content: markdownContent.substring(0, maxContentSize) + "\n\n...[truncated]",
|
|
1611
|
+
truncated: true,
|
|
1612
|
+
fullSize: markdownContent.length,
|
|
1613
|
+
note: "Content truncated. Full content available via buffer_get_items(bufferId, keys=['content']).",
|
|
1614
|
+
});
|
|
1467
1615
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
catch {
|
|
1478
|
-
// Fallback: wrap as JSON object if parsing fails
|
|
1479
|
-
const wrapperData = { content: output };
|
|
1480
|
-
const { content: jsonContent, structure: jsonStruct } = createJsonStructure(wrapperData);
|
|
1481
|
-
newBufferId = contentBuffer.storeJsonWithStructure(jsonContent, jsonStruct, bufferMetadata);
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
else {
|
|
1485
|
-
// CSV/Markdown output - wrap in JSON object for accessor support
|
|
1486
|
-
const wrapperData = { content: output, format: outputType };
|
|
1487
|
-
const { content: jsonContent, structure: jsonStruct } = createJsonStructure(wrapperData);
|
|
1488
|
-
newBufferId = contentBuffer.storeJsonWithStructure(jsonContent, jsonStruct, bufferMetadata);
|
|
1489
|
-
}
|
|
1616
|
+
// Handle single JSON output
|
|
1617
|
+
if (transformResult.jsonContent !== undefined) {
|
|
1618
|
+
const bufferId = await storeTransformOutput("json", transformResult.jsonContent, {});
|
|
1619
|
+
return formatSuccessDirect({
|
|
1620
|
+
bufferId,
|
|
1621
|
+
contentType: "json",
|
|
1622
|
+
itemCount: transformResult.itemCount,
|
|
1623
|
+
hint: "Use buffer_get_items(bufferId) to access data, or buffer_transform for further processing.",
|
|
1624
|
+
});
|
|
1490
1625
|
}
|
|
1491
|
-
//
|
|
1492
|
-
return
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1626
|
+
// Should not reach here
|
|
1627
|
+
return formatError({
|
|
1628
|
+
error: true,
|
|
1629
|
+
message: "Unexpected transform result: no output generated",
|
|
1630
|
+
statusCode: 500,
|
|
1496
1631
|
});
|
|
1497
1632
|
}
|
|
1498
1633
|
catch (error) {
|
|
@@ -1502,4 +1637,36 @@ Run help(topic="buffers") for full schema, operators, and examples.`,
|
|
|
1502
1637
|
},
|
|
1503
1638
|
};
|
|
1504
1639
|
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Store transform output as a buffer
|
|
1642
|
+
*/
|
|
1643
|
+
async function storeTransformOutput(contentType, content, metadata) {
|
|
1644
|
+
const bufferMetadata = {
|
|
1645
|
+
contentType,
|
|
1646
|
+
resourceType: "transform_output",
|
|
1647
|
+
...metadata,
|
|
1648
|
+
};
|
|
1649
|
+
if (contentType === "xhtml") {
|
|
1650
|
+
const xhtmlContent = content;
|
|
1651
|
+
const parseResult = parseXhtml(xhtmlContent);
|
|
1652
|
+
if (parseResult.document) {
|
|
1653
|
+
const structureResult = parseStructure(parseResult.document);
|
|
1654
|
+
const contentWithIds = serializeXhtml(parseResult.document);
|
|
1655
|
+
return getSessionBuffer().storeWithStructure(contentWithIds, structureResult.structure, structureResult.nextId, bufferMetadata);
|
|
1656
|
+
}
|
|
1657
|
+
return getSessionBuffer().store(xhtmlContent, bufferMetadata);
|
|
1658
|
+
}
|
|
1659
|
+
if (contentType === "markdown") {
|
|
1660
|
+
// Store markdown as a JSON wrapper for easy access via buffer_get_items
|
|
1661
|
+
const markdownContent = content;
|
|
1662
|
+
const wrapper = { content: markdownContent, format: "markdown" };
|
|
1663
|
+
const { content: jsonContent, structure: jsonStruct } = createJsonStructure(wrapper);
|
|
1664
|
+
// Override contentType to "json" so buffer_get_items can read it
|
|
1665
|
+
const markdownMetadata = { ...bufferMetadata, contentType: "json", originalType: "markdown" };
|
|
1666
|
+
return getSessionBuffer().storeJsonWithStructure(jsonContent, jsonStruct, markdownMetadata);
|
|
1667
|
+
}
|
|
1668
|
+
// JSON content
|
|
1669
|
+
const { content: jsonContent, structure: jsonStruct } = createJsonStructure(content);
|
|
1670
|
+
return getSessionBuffer().storeJsonWithStructure(jsonContent, jsonStruct, bufferMetadata);
|
|
1671
|
+
}
|
|
1505
1672
|
//# sourceMappingURL=buffer-tools.js.map
|