bitrix24-tasks-mcp-server 1.5.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/build/bitrix24/client.d.ts +75 -0
- package/build/bitrix24/client.d.ts.map +1 -1
- package/build/bitrix24/client.js +250 -2
- package/build/bitrix24/client.js.map +1 -1
- package/build/bitrix24/compositeLock.d.ts +2 -0
- package/build/bitrix24/compositeLock.d.ts.map +1 -0
- package/build/bitrix24/compositeLock.js +217 -0
- package/build/bitrix24/compositeLock.js.map +1 -0
- package/build/bitrix24/compositePublisher.d.ts +74 -0
- package/build/bitrix24/compositePublisher.d.ts.map +1 -0
- package/build/bitrix24/compositePublisher.js +288 -0
- package/build/bitrix24/compositePublisher.js.map +1 -0
- package/build/index.js +3 -2
- package/build/index.js.map +1 -1
- package/build/tools/index.d.ts +10 -1
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +605 -8
- package/build/tools/index.js.map +1 -1
- package/build/tools/validation.d.ts +3 -0
- package/build/tools/validation.d.ts.map +1 -0
- package/build/tools/validation.js +141 -0
- package/build/tools/validation.js.map +1 -0
- package/build/utils/toolLogging.d.ts +2 -0
- package/build/utils/toolLogging.d.ts.map +1 -0
- package/build/utils/toolLogging.js +93 -0
- package/build/utils/toolLogging.js.map +1 -0
- package/package.json +3 -3
package/build/tools/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { bitrix24Client } from '../bitrix24/client.js';
|
|
3
|
+
import { publishTaskComposite } from '../bitrix24/compositePublisher.js';
|
|
4
|
+
import { validateToolArguments } from './validation.js';
|
|
2
5
|
function stripBase64FromDownloadedFile(file) {
|
|
3
6
|
if (!file.base64) {
|
|
4
7
|
return file;
|
|
@@ -13,6 +16,45 @@ function stripBase64FromDownloadedFile(file) {
|
|
|
13
16
|
function collectFilesWithContent(...groups) {
|
|
14
17
|
return groups.flat().filter((file) => file.base64);
|
|
15
18
|
}
|
|
19
|
+
function normalizeStageTitle(value) {
|
|
20
|
+
return String(value ?? '').trim().toLocaleLowerCase('ru-RU');
|
|
21
|
+
}
|
|
22
|
+
function compareNumericIds(left, right) {
|
|
23
|
+
if (!/^\d+$/.test(left) || !/^\d+$/.test(right)) {
|
|
24
|
+
return left.localeCompare(right, 'en', { numeric: true });
|
|
25
|
+
}
|
|
26
|
+
const leftValue = BigInt(left);
|
|
27
|
+
const rightValue = BigInt(right);
|
|
28
|
+
return leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0;
|
|
29
|
+
}
|
|
30
|
+
function compactText(value, maxChars) {
|
|
31
|
+
if (value === undefined || value.length <= maxChars) {
|
|
32
|
+
return {
|
|
33
|
+
text: value,
|
|
34
|
+
truncated: false,
|
|
35
|
+
originalChars: value?.length ?? 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
text: value.slice(0, maxChars),
|
|
40
|
+
truncated: true,
|
|
41
|
+
originalChars: value.length,
|
|
42
|
+
sha256: createHash('sha256').update(value).digest('hex')
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function boundedOptionalString(value, maxChars) {
|
|
46
|
+
if (value === undefined || value === null) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return String(value).slice(0, maxChars);
|
|
50
|
+
}
|
|
51
|
+
function boundErrors(errors) {
|
|
52
|
+
return {
|
|
53
|
+
errors: errors.slice(0, 10).map((error) => String(error).slice(0, 512)),
|
|
54
|
+
errorsTruncated: errors.length > 10 || errors.some((error) => String(error).length > 512),
|
|
55
|
+
originalErrorCount: errors.length
|
|
56
|
+
};
|
|
57
|
+
}
|
|
16
58
|
// Task Management Tools
|
|
17
59
|
export const createTaskTool = {
|
|
18
60
|
name: 'bitrix24_create_task',
|
|
@@ -122,6 +164,65 @@ export const getTaskTool = {
|
|
|
122
164
|
required: ['id']
|
|
123
165
|
}
|
|
124
166
|
};
|
|
167
|
+
export const getTaskSummaryTool = {
|
|
168
|
+
name: 'bitrix24_get_task_summary',
|
|
169
|
+
description: 'Read a strict bounded task projection for workflow intake and routing. DESCRIPTION is capped at 12000 characters; TITLE is display metadata and must not be used as a routing signal.',
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
additionalProperties: false,
|
|
173
|
+
properties: {
|
|
174
|
+
taskId: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
minLength: 1,
|
|
177
|
+
maxLength: 32,
|
|
178
|
+
pattern: '^\\d+$',
|
|
179
|
+
description: 'Task ID'
|
|
180
|
+
},
|
|
181
|
+
select: {
|
|
182
|
+
type: 'array',
|
|
183
|
+
minItems: 1,
|
|
184
|
+
maxItems: 12,
|
|
185
|
+
items: {
|
|
186
|
+
type: 'string',
|
|
187
|
+
enum: [
|
|
188
|
+
'ID',
|
|
189
|
+
'TITLE',
|
|
190
|
+
'DESCRIPTION',
|
|
191
|
+
'GROUP_ID',
|
|
192
|
+
'STAGE_ID',
|
|
193
|
+
'STATUS',
|
|
194
|
+
'CHANGED_DATE',
|
|
195
|
+
'CREATED_DATE',
|
|
196
|
+
'RESPONSIBLE_ID',
|
|
197
|
+
'DEADLINE',
|
|
198
|
+
'PRIORITY',
|
|
199
|
+
'PARENT_ID'
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
description: 'Optional Bitrix task fields. Defaults to ID, TITLE, DESCRIPTION, GROUP_ID, STAGE_ID, STATUS, CHANGED_DATE, RESPONSIBLE_ID.'
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
required: ['taskId']
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
export const getTaskStageTool = {
|
|
209
|
+
name: 'bitrix24_get_task_stage',
|
|
210
|
+
description: 'Read the current task stage and minimal routing metadata without loading comments, description, or files.',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
additionalProperties: false,
|
|
214
|
+
properties: {
|
|
215
|
+
taskId: {
|
|
216
|
+
type: 'string',
|
|
217
|
+
minLength: 1,
|
|
218
|
+
maxLength: 32,
|
|
219
|
+
pattern: '^\\d+$',
|
|
220
|
+
description: 'Task ID'
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
required: ['taskId']
|
|
224
|
+
}
|
|
225
|
+
};
|
|
125
226
|
export const listTasksTool = {
|
|
126
227
|
name: 'bitrix24_list_tasks',
|
|
127
228
|
description: 'List tasks with optional filtering and ordering',
|
|
@@ -203,7 +304,7 @@ export const getTaskCommentsTool = {
|
|
|
203
304
|
description: 'Legacy comment FILTER (task.commentitem.getlist only)'
|
|
204
305
|
},
|
|
205
306
|
limit: {
|
|
206
|
-
type: '
|
|
307
|
+
type: 'integer',
|
|
207
308
|
description: 'Max messages for chat API (1-50, default 50)',
|
|
208
309
|
default: 50
|
|
209
310
|
},
|
|
@@ -247,17 +348,86 @@ export const getTaskCommentsTool = {
|
|
|
247
348
|
required: ['taskId']
|
|
248
349
|
}
|
|
249
350
|
};
|
|
351
|
+
export const getTaskCommentsAfterTool = {
|
|
352
|
+
name: 'bitrix24_get_comments_after',
|
|
353
|
+
description: 'Read compact comments newer than a known comment/message ID. Returns text and metadata only; never downloads attachments or file bytes.',
|
|
354
|
+
inputSchema: {
|
|
355
|
+
type: 'object',
|
|
356
|
+
additionalProperties: false,
|
|
357
|
+
properties: {
|
|
358
|
+
taskId: {
|
|
359
|
+
type: 'string',
|
|
360
|
+
minLength: 1,
|
|
361
|
+
maxLength: 32,
|
|
362
|
+
pattern: '^\\d+$',
|
|
363
|
+
description: 'Task ID'
|
|
364
|
+
},
|
|
365
|
+
afterCommentId: {
|
|
366
|
+
type: 'string',
|
|
367
|
+
minLength: 1,
|
|
368
|
+
maxLength: 32,
|
|
369
|
+
pattern: '^\\d+$',
|
|
370
|
+
description: 'Return only comments/messages whose numeric ID is greater than this watermark'
|
|
371
|
+
},
|
|
372
|
+
preferApi: {
|
|
373
|
+
type: 'string',
|
|
374
|
+
enum: ['auto', 'chat', 'legacy'],
|
|
375
|
+
description: 'API strategy',
|
|
376
|
+
default: 'auto'
|
|
377
|
+
},
|
|
378
|
+
limit: {
|
|
379
|
+
type: 'integer',
|
|
380
|
+
minimum: 1,
|
|
381
|
+
maximum: 20,
|
|
382
|
+
description: 'Maximum comments to inspect and return',
|
|
383
|
+
default: 20
|
|
384
|
+
},
|
|
385
|
+
includeSystemMessages: {
|
|
386
|
+
type: 'boolean',
|
|
387
|
+
description: 'Include system messages',
|
|
388
|
+
default: false
|
|
389
|
+
},
|
|
390
|
+
maxCommentChars: {
|
|
391
|
+
type: 'integer',
|
|
392
|
+
minimum: 256,
|
|
393
|
+
maximum: 8000,
|
|
394
|
+
description: 'Per-comment text cap. Truncated comments include originalChars and sha256.',
|
|
395
|
+
default: 4000
|
|
396
|
+
},
|
|
397
|
+
maxTotalChars: {
|
|
398
|
+
type: 'integer',
|
|
399
|
+
minimum: 512,
|
|
400
|
+
maximum: 32000,
|
|
401
|
+
description: 'Hard cap for text across the returned page.',
|
|
402
|
+
default: 16000
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
required: ['taskId', 'afterCommentId']
|
|
406
|
+
}
|
|
407
|
+
};
|
|
250
408
|
export const addTaskCommentTool = {
|
|
251
409
|
name: 'bitrix24_add_task_comment',
|
|
252
410
|
description: 'Post a comment/message to a task. Modern path: tasks.task.chat.message.send + im.disk.file.commit; legacy fallback: task.commentitem.add.',
|
|
253
411
|
inputSchema: {
|
|
254
412
|
type: 'object',
|
|
413
|
+
additionalProperties: false,
|
|
255
414
|
properties: {
|
|
256
|
-
taskId: {
|
|
257
|
-
|
|
415
|
+
taskId: {
|
|
416
|
+
type: 'string',
|
|
417
|
+
minLength: 1,
|
|
418
|
+
maxLength: 32,
|
|
419
|
+
pattern: '^\\d+$',
|
|
420
|
+
description: 'Task ID'
|
|
421
|
+
},
|
|
422
|
+
text: {
|
|
423
|
+
type: 'string',
|
|
424
|
+
maxLength: 32000,
|
|
425
|
+
description: 'Comment text (required if no filePaths)'
|
|
426
|
+
},
|
|
258
427
|
filePaths: {
|
|
259
428
|
type: 'array',
|
|
260
|
-
|
|
429
|
+
maxItems: 12,
|
|
430
|
+
items: { type: 'string', minLength: 1, maxLength: 4096 },
|
|
261
431
|
description: 'Local files to upload and attach to the comment/message'
|
|
262
432
|
},
|
|
263
433
|
diskFolderId: {
|
|
@@ -308,16 +478,141 @@ export const getTaskFilesTool = {
|
|
|
308
478
|
required: ['taskId']
|
|
309
479
|
}
|
|
310
480
|
};
|
|
481
|
+
export const getTaskFilesMetadataTool = {
|
|
482
|
+
name: 'bitrix24_get_task_files_metadata',
|
|
483
|
+
description: 'Read one bounded page of task attachment metadata without downloading file bytes or emitting base64.',
|
|
484
|
+
inputSchema: {
|
|
485
|
+
type: 'object',
|
|
486
|
+
additionalProperties: false,
|
|
487
|
+
properties: {
|
|
488
|
+
taskId: {
|
|
489
|
+
type: 'string',
|
|
490
|
+
minLength: 1,
|
|
491
|
+
maxLength: 32,
|
|
492
|
+
pattern: '^\\d+$',
|
|
493
|
+
description: 'Task ID'
|
|
494
|
+
},
|
|
495
|
+
diskFileIds: {
|
|
496
|
+
type: 'array',
|
|
497
|
+
maxItems: 50,
|
|
498
|
+
items: { type: 'string', minLength: 1, maxLength: 64 },
|
|
499
|
+
description: 'Optional exact disk file IDs; omitted means all task file references'
|
|
500
|
+
},
|
|
501
|
+
imagesOnly: {
|
|
502
|
+
type: 'boolean',
|
|
503
|
+
description: 'Return image metadata only',
|
|
504
|
+
default: false
|
|
505
|
+
},
|
|
506
|
+
afterFileId: {
|
|
507
|
+
type: 'string',
|
|
508
|
+
minLength: 1,
|
|
509
|
+
maxLength: 64,
|
|
510
|
+
description: 'Pagination watermark from nextAfterFileId'
|
|
511
|
+
},
|
|
512
|
+
limit: {
|
|
513
|
+
type: 'integer',
|
|
514
|
+
minimum: 1,
|
|
515
|
+
maximum: 25,
|
|
516
|
+
default: 25,
|
|
517
|
+
description: 'Maximum metadata records in one page'
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
required: ['taskId']
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
export const publishTaskCompositeTool = {
|
|
524
|
+
name: 'bitrix24_publish_task_composite',
|
|
525
|
+
description: 'Idempotently publish a bounded ordered set of final comments/evidence attachments and then move the task to one exact stage with read-back verification. Use one call for final workflow publication.',
|
|
526
|
+
inputSchema: {
|
|
527
|
+
type: 'object',
|
|
528
|
+
additionalProperties: false,
|
|
529
|
+
properties: {
|
|
530
|
+
taskId: {
|
|
531
|
+
type: 'string',
|
|
532
|
+
minLength: 1,
|
|
533
|
+
maxLength: 32,
|
|
534
|
+
pattern: '^\\d+$',
|
|
535
|
+
description: 'Task ID'
|
|
536
|
+
},
|
|
537
|
+
runId: {
|
|
538
|
+
type: 'string',
|
|
539
|
+
minLength: 1,
|
|
540
|
+
maxLength: 256,
|
|
541
|
+
description: 'Stable workflow run/dispatch ID'
|
|
542
|
+
},
|
|
543
|
+
evidenceSha256: {
|
|
544
|
+
type: 'string',
|
|
545
|
+
pattern: '^[a-fA-F0-9]{64}$',
|
|
546
|
+
description: 'SHA-256 of the evidence manifest being published'
|
|
547
|
+
},
|
|
548
|
+
comments: {
|
|
549
|
+
type: 'array',
|
|
550
|
+
maxItems: 12,
|
|
551
|
+
items: {
|
|
552
|
+
type: 'object',
|
|
553
|
+
additionalProperties: false,
|
|
554
|
+
properties: {
|
|
555
|
+
text: { type: 'string', minLength: 1, maxLength: 32000 },
|
|
556
|
+
filePaths: {
|
|
557
|
+
type: 'array',
|
|
558
|
+
maxItems: 12,
|
|
559
|
+
items: { type: 'string', minLength: 1, maxLength: 4096 },
|
|
560
|
+
description: 'Optional local evidence files for this comment'
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
required: ['text']
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
targetStageId: {
|
|
567
|
+
type: 'string',
|
|
568
|
+
minLength: 1,
|
|
569
|
+
maxLength: 64,
|
|
570
|
+
description: 'Exact target stage ID'
|
|
571
|
+
},
|
|
572
|
+
expectedCurrentStageId: {
|
|
573
|
+
type: 'string',
|
|
574
|
+
minLength: 1,
|
|
575
|
+
maxLength: 64,
|
|
576
|
+
description: 'Optional compare-and-set guard for the current stage'
|
|
577
|
+
},
|
|
578
|
+
preferApi: {
|
|
579
|
+
type: 'string',
|
|
580
|
+
enum: ['auto', 'chat', 'legacy'],
|
|
581
|
+
default: 'auto'
|
|
582
|
+
},
|
|
583
|
+
diskFolderId: {
|
|
584
|
+
type: 'string',
|
|
585
|
+
description: 'Optional Bitrix Disk folder for evidence uploads'
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
required: [
|
|
589
|
+
'taskId',
|
|
590
|
+
'runId',
|
|
591
|
+
'evidenceSha256',
|
|
592
|
+
'comments',
|
|
593
|
+
'targetStageId'
|
|
594
|
+
]
|
|
595
|
+
}
|
|
596
|
+
};
|
|
311
597
|
export const attachFilesToTaskTool = {
|
|
312
598
|
name: 'bitrix24_attach_files_to_task',
|
|
313
599
|
description: 'Upload local files (images, documents) to Bitrix24 Disk and attach them to an existing task',
|
|
314
600
|
inputSchema: {
|
|
315
601
|
type: 'object',
|
|
602
|
+
additionalProperties: false,
|
|
316
603
|
properties: {
|
|
317
|
-
taskId: {
|
|
604
|
+
taskId: {
|
|
605
|
+
type: 'string',
|
|
606
|
+
minLength: 1,
|
|
607
|
+
maxLength: 32,
|
|
608
|
+
pattern: '^\\d+$',
|
|
609
|
+
description: 'Task ID'
|
|
610
|
+
},
|
|
318
611
|
filePaths: {
|
|
319
612
|
type: 'array',
|
|
320
|
-
|
|
613
|
+
minItems: 1,
|
|
614
|
+
maxItems: 12,
|
|
615
|
+
items: { type: 'string', minLength: 1, maxLength: 4096 },
|
|
321
616
|
description: 'Local file paths to upload and attach'
|
|
322
617
|
},
|
|
323
618
|
diskFolderId: {
|
|
@@ -355,6 +650,92 @@ export const updateTaskTool = {
|
|
|
355
650
|
required: ['id']
|
|
356
651
|
}
|
|
357
652
|
};
|
|
653
|
+
export const getTaskStagesTool = {
|
|
654
|
+
name: 'bitrix24_get_task_stages',
|
|
655
|
+
description: 'Get Kanban/My Plan stages for a Bitrix24 task group or user plan',
|
|
656
|
+
inputSchema: {
|
|
657
|
+
type: 'object',
|
|
658
|
+
properties: {
|
|
659
|
+
entityId: {
|
|
660
|
+
type: 'string',
|
|
661
|
+
description: 'Group/project ID for Kanban stages, or user ID for My Plan stages'
|
|
662
|
+
},
|
|
663
|
+
entityType: {
|
|
664
|
+
type: 'string',
|
|
665
|
+
enum: ['G', 'U'],
|
|
666
|
+
description: 'G=group/project Kanban, U=user My Plan',
|
|
667
|
+
default: 'G'
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
required: ['entityId']
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
export const canMoveTaskInStagesTool = {
|
|
674
|
+
name: 'bitrix24_can_move_task_in_stages',
|
|
675
|
+
description: 'Check whether the webhook user can move tasks in a Bitrix24 Kanban/My Plan object',
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: 'object',
|
|
678
|
+
properties: {
|
|
679
|
+
entityId: {
|
|
680
|
+
type: 'string',
|
|
681
|
+
description: 'Group/project ID for Kanban stages, or user ID for My Plan stages'
|
|
682
|
+
},
|
|
683
|
+
entityType: {
|
|
684
|
+
type: 'string',
|
|
685
|
+
enum: ['G', 'U'],
|
|
686
|
+
description: 'G=group/project Kanban, U=user My Plan',
|
|
687
|
+
default: 'G'
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
required: ['entityId']
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
export const moveTaskToStageTool = {
|
|
694
|
+
name: 'bitrix24_move_task_to_stage',
|
|
695
|
+
description: 'Move a task to another Kanban/My Plan stage using task.stages.movetask. Provide stageId directly or stageName with entityId.',
|
|
696
|
+
inputSchema: {
|
|
697
|
+
type: 'object',
|
|
698
|
+
additionalProperties: false,
|
|
699
|
+
properties: {
|
|
700
|
+
taskId: {
|
|
701
|
+
type: 'string',
|
|
702
|
+
minLength: 1,
|
|
703
|
+
maxLength: 32,
|
|
704
|
+
pattern: '^\\d+$',
|
|
705
|
+
description: 'Task ID'
|
|
706
|
+
},
|
|
707
|
+
stageId: {
|
|
708
|
+
type: 'string',
|
|
709
|
+
minLength: 1,
|
|
710
|
+
maxLength: 64,
|
|
711
|
+
description: 'Target stage ID'
|
|
712
|
+
},
|
|
713
|
+
stageName: {
|
|
714
|
+
type: 'string',
|
|
715
|
+
description: 'Target stage title. Used to resolve stageId when stageId is omitted.'
|
|
716
|
+
},
|
|
717
|
+
entityId: {
|
|
718
|
+
type: 'string',
|
|
719
|
+
description: 'Group/project ID for Kanban stages, or user ID for My Plan stages. Required when using stageName.'
|
|
720
|
+
},
|
|
721
|
+
entityType: {
|
|
722
|
+
type: 'string',
|
|
723
|
+
enum: ['G', 'U'],
|
|
724
|
+
description: 'G=group/project Kanban, U=user My Plan',
|
|
725
|
+
default: 'G'
|
|
726
|
+
},
|
|
727
|
+
before: {
|
|
728
|
+
type: 'string',
|
|
729
|
+
description: 'Optional task ID before which this task should be placed'
|
|
730
|
+
},
|
|
731
|
+
after: {
|
|
732
|
+
type: 'string',
|
|
733
|
+
description: 'Optional task ID after which this task should be placed'
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
required: ['taskId']
|
|
737
|
+
}
|
|
738
|
+
};
|
|
358
739
|
export const addTaskChecklistItemTool = {
|
|
359
740
|
name: 'bitrix24_add_task_checklist_item',
|
|
360
741
|
description: 'Add a checklist item to a task',
|
|
@@ -618,16 +999,24 @@ export const resolveUserNamesTool = {
|
|
|
618
999
|
required: ['userIds']
|
|
619
1000
|
}
|
|
620
1001
|
};
|
|
621
|
-
|
|
622
|
-
export const allTools = [
|
|
1002
|
+
const allToolCatalog = [
|
|
623
1003
|
createTaskTool,
|
|
1004
|
+
getTaskSummaryTool,
|
|
1005
|
+
getTaskStageTool,
|
|
624
1006
|
getTaskTool,
|
|
625
1007
|
listTasksTool,
|
|
626
1008
|
getLatestTasksTool,
|
|
627
1009
|
getTasksFromDateRangeTool,
|
|
1010
|
+
getTaskCommentsTool,
|
|
1011
|
+
getTaskCommentsAfterTool,
|
|
628
1012
|
updateTaskTool,
|
|
1013
|
+
getTaskStagesTool,
|
|
1014
|
+
canMoveTaskInStagesTool,
|
|
1015
|
+
moveTaskToStageTool,
|
|
629
1016
|
getTaskFilesTool,
|
|
1017
|
+
getTaskFilesMetadataTool,
|
|
630
1018
|
addTaskCommentTool,
|
|
1019
|
+
publishTaskCompositeTool,
|
|
631
1020
|
attachFilesToTaskTool,
|
|
632
1021
|
addTaskChecklistItemTool,
|
|
633
1022
|
listTaskChecklistItemsTool,
|
|
@@ -647,8 +1036,35 @@ export const allTools = [
|
|
|
647
1036
|
getAllUsersTool,
|
|
648
1037
|
resolveUserNamesTool
|
|
649
1038
|
];
|
|
1039
|
+
const WORKFLOW_TOOL_NAMES = new Set([
|
|
1040
|
+
'bitrix24_get_task_summary',
|
|
1041
|
+
'bitrix24_get_task_stage',
|
|
1042
|
+
'bitrix24_get_comments_after',
|
|
1043
|
+
'bitrix24_get_task_files_metadata',
|
|
1044
|
+
'bitrix24_publish_task_composite',
|
|
1045
|
+
'bitrix24_add_task_comment',
|
|
1046
|
+
'bitrix24_attach_files_to_task',
|
|
1047
|
+
'bitrix24_move_task_to_stage'
|
|
1048
|
+
]);
|
|
1049
|
+
export function toolsForProfile(profile = process.env.BITRIX24_TOOL_PROFILE) {
|
|
1050
|
+
const normalized = profile?.trim().toLowerCase();
|
|
1051
|
+
if (!normalized || normalized === 'full') {
|
|
1052
|
+
return [...allToolCatalog];
|
|
1053
|
+
}
|
|
1054
|
+
if (normalized === 'workflow') {
|
|
1055
|
+
return allToolCatalog.filter((tool) => WORKFLOW_TOOL_NAMES.has(tool.name));
|
|
1056
|
+
}
|
|
1057
|
+
throw new Error(`Unsupported BITRIX24_TOOL_PROFILE ${JSON.stringify(profile)}; expected full or workflow`);
|
|
1058
|
+
}
|
|
1059
|
+
/** Tools exposed via MCP ListTools for the configured deployment profile. */
|
|
1060
|
+
export const allTools = toolsForProfile();
|
|
650
1061
|
export async function executeToolCall(name, args) {
|
|
651
1062
|
try {
|
|
1063
|
+
const tool = allTools.find((candidate) => candidate.name === name);
|
|
1064
|
+
if (!tool) {
|
|
1065
|
+
throw new Error(`Tool is not exposed by the active profile: ${name}`);
|
|
1066
|
+
}
|
|
1067
|
+
args = validateToolArguments(tool, args ?? {});
|
|
652
1068
|
switch (name) {
|
|
653
1069
|
case 'bitrix24_create_task': {
|
|
654
1070
|
const task = {
|
|
@@ -715,6 +1131,88 @@ export async function executeToolCall(name, args) {
|
|
|
715
1131
|
message: `Task created with ID: ${taskId}`
|
|
716
1132
|
};
|
|
717
1133
|
}
|
|
1134
|
+
case 'bitrix24_get_task_summary': {
|
|
1135
|
+
const task = await bitrix24Client.getTaskSummary(args.taskId, Array.isArray(args.select) && args.select.length > 0
|
|
1136
|
+
? args.select
|
|
1137
|
+
: undefined);
|
|
1138
|
+
return {
|
|
1139
|
+
success: true,
|
|
1140
|
+
task
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
case 'bitrix24_get_task_stage': {
|
|
1144
|
+
const stage = await bitrix24Client.getTaskStage(args.taskId);
|
|
1145
|
+
return {
|
|
1146
|
+
success: true,
|
|
1147
|
+
...stage
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
case 'bitrix24_get_comments_after': {
|
|
1151
|
+
const afterCommentId = String(args.afterCommentId);
|
|
1152
|
+
const afterCommentNumber = Number(afterCommentId);
|
|
1153
|
+
if (!Number.isSafeInteger(afterCommentNumber)) {
|
|
1154
|
+
throw new Error('afterCommentId exceeds the supported safe integer range');
|
|
1155
|
+
}
|
|
1156
|
+
const limit = Math.min(20, Math.max(1, Math.trunc(Number(args.limit ?? 20))));
|
|
1157
|
+
const maxCommentChars = Math.min(8_000, Math.max(256, Number(args.maxCommentChars ?? 4_000)));
|
|
1158
|
+
const maxTotalChars = Math.min(32_000, Math.max(512, Number(args.maxTotalChars ?? 16_000)));
|
|
1159
|
+
const commentsResult = await bitrix24Client.getTaskComments(args.taskId, {
|
|
1160
|
+
preferApi: args.preferApi,
|
|
1161
|
+
firstId: afterCommentNumber,
|
|
1162
|
+
limit: Math.min(50, limit + 1),
|
|
1163
|
+
includeSystemMessages: args.includeSystemMessages === true,
|
|
1164
|
+
includeAttachments: false,
|
|
1165
|
+
includeContent: false
|
|
1166
|
+
});
|
|
1167
|
+
const candidates = commentsResult.comments
|
|
1168
|
+
.filter((comment) => /^\d{1,32}$/.test(String(comment.id)))
|
|
1169
|
+
.filter((comment) => compareNumericIds(comment.id, afterCommentId) > 0)
|
|
1170
|
+
.sort((left, right) => compareNumericIds(left.id, right.id));
|
|
1171
|
+
const comments = [];
|
|
1172
|
+
let totalCommentChars = 0;
|
|
1173
|
+
for (const comment of candidates) {
|
|
1174
|
+
if (comments.length >= limit) {
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
1177
|
+
const remainingChars = maxTotalChars - totalCommentChars;
|
|
1178
|
+
if (remainingChars <= 0) {
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
const effectiveCap = Math.max(1, Math.min(maxCommentChars, remainingChars));
|
|
1182
|
+
const compacted = compactText(comment.postMessage, effectiveCap);
|
|
1183
|
+
const textLength = compacted.text?.length ?? 0;
|
|
1184
|
+
if (comments.length > 0 && textLength > remainingChars) {
|
|
1185
|
+
break;
|
|
1186
|
+
}
|
|
1187
|
+
comments.push({
|
|
1188
|
+
id: comment.id,
|
|
1189
|
+
authorId: boundedOptionalString(comment.authorId, 64),
|
|
1190
|
+
authorName: boundedOptionalString(comment.authorName, 256),
|
|
1191
|
+
postDate: boundedOptionalString(comment.postDate, 128),
|
|
1192
|
+
postMessage: compacted.text,
|
|
1193
|
+
truncated: compacted.truncated,
|
|
1194
|
+
originalChars: compacted.originalChars,
|
|
1195
|
+
sha256: compacted.sha256
|
|
1196
|
+
});
|
|
1197
|
+
totalCommentChars += textLength;
|
|
1198
|
+
}
|
|
1199
|
+
const nextAfterCommentId = comments.length > 0
|
|
1200
|
+
? String(comments[comments.length - 1].id)
|
|
1201
|
+
: afterCommentId;
|
|
1202
|
+
const boundedErrors = boundErrors(commentsResult.errors);
|
|
1203
|
+
return {
|
|
1204
|
+
success: commentsResult.errors.length === 0,
|
|
1205
|
+
taskId: commentsResult.taskId,
|
|
1206
|
+
apiMode: commentsResult.apiMode,
|
|
1207
|
+
afterCommentId,
|
|
1208
|
+
nextAfterCommentId,
|
|
1209
|
+
hasMore: candidates.some((comment) => compareNumericIds(comment.id, nextAfterCommentId) > 0),
|
|
1210
|
+
pageSize: comments.length,
|
|
1211
|
+
totalCommentChars,
|
|
1212
|
+
comments,
|
|
1213
|
+
...boundedErrors
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
718
1216
|
case 'bitrix24_get_task_comments': {
|
|
719
1217
|
const order = {};
|
|
720
1218
|
order[args.orderBy || 'POST_DATE'] = args.orderDirection || 'asc';
|
|
@@ -812,6 +1310,25 @@ export async function executeToolCall(name, args) {
|
|
|
812
1310
|
: `Retrieved ${filesResult.files.length} file(s) for task ${args.taskId}`
|
|
813
1311
|
};
|
|
814
1312
|
}
|
|
1313
|
+
case 'bitrix24_get_task_files_metadata': {
|
|
1314
|
+
const filesResult = await bitrix24Client.getTaskFilesMetadata(args.taskId, {
|
|
1315
|
+
diskFileIds: args.diskFileIds,
|
|
1316
|
+
imagesOnly: args.imagesOnly === true,
|
|
1317
|
+
afterFileId: args.afterFileId,
|
|
1318
|
+
limit: args.limit
|
|
1319
|
+
});
|
|
1320
|
+
const boundedErrors = boundErrors(filesResult.errors);
|
|
1321
|
+
return {
|
|
1322
|
+
success: filesResult.errors.length === 0,
|
|
1323
|
+
taskId: filesResult.taskId,
|
|
1324
|
+
afterFileId: filesResult.afterFileId,
|
|
1325
|
+
nextAfterFileId: filesResult.nextAfterFileId,
|
|
1326
|
+
hasMore: filesResult.hasMore,
|
|
1327
|
+
pageSize: filesResult.files.length,
|
|
1328
|
+
files: filesResult.files,
|
|
1329
|
+
...boundedErrors
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
815
1332
|
case 'bitrix24_attach_files_to_task': {
|
|
816
1333
|
const attachmentResult = await bitrix24Client.attachLocalFilesToTask(args.taskId, args.filePaths, args.diskFolderId);
|
|
817
1334
|
return {
|
|
@@ -824,6 +1341,22 @@ export async function executeToolCall(name, args) {
|
|
|
824
1341
|
: `Attached ${attachmentResult.attached.length} file(s) with ${attachmentResult.errors.length} error(s)`
|
|
825
1342
|
};
|
|
826
1343
|
}
|
|
1344
|
+
case 'bitrix24_publish_task_composite': {
|
|
1345
|
+
const receipt = await publishTaskComposite(bitrix24Client, {
|
|
1346
|
+
taskId: args.taskId,
|
|
1347
|
+
runId: args.runId,
|
|
1348
|
+
evidenceSha256: args.evidenceSha256,
|
|
1349
|
+
comments: args.comments,
|
|
1350
|
+
targetStageId: args.targetStageId,
|
|
1351
|
+
expectedCurrentStageId: args.expectedCurrentStageId,
|
|
1352
|
+
preferApi: args.preferApi,
|
|
1353
|
+
diskFolderId: args.diskFolderId
|
|
1354
|
+
});
|
|
1355
|
+
return {
|
|
1356
|
+
success: true,
|
|
1357
|
+
receipt
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
827
1360
|
case 'bitrix24_get_task': {
|
|
828
1361
|
const details = await bitrix24Client.getTaskDetails(args.id, {
|
|
829
1362
|
includeComments: args.includeComments,
|
|
@@ -924,6 +1457,70 @@ export async function executeToolCall(name, args) {
|
|
|
924
1457
|
const updated = await bitrix24Client.updateTask(args.id, updateTask);
|
|
925
1458
|
return { success: true, updated, message: `Task ${args.id} updated successfully` };
|
|
926
1459
|
}
|
|
1460
|
+
case 'bitrix24_get_task_stages': {
|
|
1461
|
+
const entityType = args.entityType === 'U' ? 'U' : 'G';
|
|
1462
|
+
const stages = await bitrix24Client.getTaskStages(args.entityId, entityType);
|
|
1463
|
+
return {
|
|
1464
|
+
success: true,
|
|
1465
|
+
entityId: args.entityId,
|
|
1466
|
+
entityType,
|
|
1467
|
+
stages,
|
|
1468
|
+
message: `Found ${stages.length} stage(s) for ${entityType}:${args.entityId}`
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
case 'bitrix24_can_move_task_in_stages': {
|
|
1472
|
+
const entityType = args.entityType === 'U' ? 'U' : 'G';
|
|
1473
|
+
const canMove = await bitrix24Client.canMoveTaskInStages(args.entityId, entityType);
|
|
1474
|
+
return {
|
|
1475
|
+
success: true,
|
|
1476
|
+
entityId: args.entityId,
|
|
1477
|
+
entityType,
|
|
1478
|
+
canMove,
|
|
1479
|
+
message: canMove
|
|
1480
|
+
? `Tasks can be moved in ${entityType}:${args.entityId}`
|
|
1481
|
+
: `Tasks cannot be moved in ${entityType}:${args.entityId}`
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
case 'bitrix24_move_task_to_stage': {
|
|
1485
|
+
const entityType = args.entityType === 'U' ? 'U' : 'G';
|
|
1486
|
+
let stageId = args.stageId;
|
|
1487
|
+
let resolvedStage;
|
|
1488
|
+
if (!stageId) {
|
|
1489
|
+
if (!args.stageName) {
|
|
1490
|
+
throw new Error('stageId or stageName is required');
|
|
1491
|
+
}
|
|
1492
|
+
if (!args.entityId) {
|
|
1493
|
+
throw new Error('entityId is required when resolving stageName');
|
|
1494
|
+
}
|
|
1495
|
+
const stages = await bitrix24Client.getTaskStages(args.entityId, entityType);
|
|
1496
|
+
const normalizedStageName = normalizeStageTitle(args.stageName);
|
|
1497
|
+
const matches = stages.filter((stage) => normalizeStageTitle(stage.TITLE) === normalizedStageName);
|
|
1498
|
+
if (matches.length === 0) {
|
|
1499
|
+
throw new Error(`Stage "${args.stageName}" not found in ${entityType}:${args.entityId}`);
|
|
1500
|
+
}
|
|
1501
|
+
if (matches.length > 1) {
|
|
1502
|
+
throw new Error(`Stage "${args.stageName}" is ambiguous in ${entityType}:${args.entityId}`);
|
|
1503
|
+
}
|
|
1504
|
+
resolvedStage = matches[0];
|
|
1505
|
+
stageId = resolvedStage.ID;
|
|
1506
|
+
}
|
|
1507
|
+
const moved = await bitrix24Client.moveTaskToStage({
|
|
1508
|
+
taskId: args.taskId,
|
|
1509
|
+
stageId,
|
|
1510
|
+
before: args.before,
|
|
1511
|
+
after: args.after
|
|
1512
|
+
});
|
|
1513
|
+
return {
|
|
1514
|
+
success: true,
|
|
1515
|
+
taskId: args.taskId,
|
|
1516
|
+
stageId,
|
|
1517
|
+
stageName: resolvedStage?.TITLE ?? args.stageName,
|
|
1518
|
+
moved,
|
|
1519
|
+
message: moved
|
|
1520
|
+
? `Task ${args.taskId} moved to stage ${stageId}`
|
|
1521
|
+
: `Move request for task ${args.taskId} returned false`
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
927
1524
|
case 'bitrix24_add_task_checklist_item': {
|
|
928
1525
|
const checklistItemId = await bitrix24Client.addChecklistItem(args.taskId, {
|
|
929
1526
|
TITLE: args.title,
|