@damper/mcp 0.3.14 ā 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formatters.d.ts +1 -0
- package/dist/formatters.js +15 -9
- package/dist/index.js +201 -24
- package/package.json +1 -1
package/dist/formatters.d.ts
CHANGED
package/dist/formatters.js
CHANGED
|
@@ -9,22 +9,29 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export function formatStartTaskResponse(result) {
|
|
11
11
|
const lines = [`ā
Started ${result.id}: ${result.message}`];
|
|
12
|
+
// Critical rules section - shown first and prominently
|
|
13
|
+
if (result.context?.criticalRules && result.context.criticalRules.length > 0) {
|
|
14
|
+
lines.push('\nšØ **CRITICAL RULES (do not skip):**');
|
|
15
|
+
for (const rule of result.context.criticalRules) {
|
|
16
|
+
lines.push(`⢠${rule}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
12
19
|
// Context section - emphatic about reading it first
|
|
13
20
|
if (result.context) {
|
|
14
21
|
if (result.context.isEmpty) {
|
|
15
22
|
lines.push(`\nš ${result.context.hint || 'No project context available.'}`);
|
|
16
23
|
}
|
|
17
24
|
else {
|
|
18
|
-
lines.push('\n
|
|
25
|
+
lines.push('\nš **Project context available.** Read sections relevant to your task:');
|
|
19
26
|
lines.push('');
|
|
20
27
|
if (result.context.relevantSections && result.context.relevantSections.length > 0) {
|
|
21
|
-
lines.push(`**
|
|
28
|
+
lines.push(`**Suggested for this task:**`);
|
|
22
29
|
for (const section of result.context.relevantSections) {
|
|
23
30
|
lines.push(`ā \`get_context_section("${section}")\``);
|
|
24
31
|
}
|
|
25
32
|
lines.push('');
|
|
26
33
|
}
|
|
27
|
-
lines.push('**All
|
|
34
|
+
lines.push('**All sections:**');
|
|
28
35
|
for (const s of result.context.index) {
|
|
29
36
|
const relevant = result.context.relevantSections?.includes(s.section) ? ' ā' : '';
|
|
30
37
|
lines.push(`⢠${s.section}${relevant}`);
|
|
@@ -32,12 +39,11 @@ export function formatStartTaskResponse(result) {
|
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
lines.push('\n---');
|
|
35
|
-
lines.push('**š
|
|
36
|
-
lines.push('1.
|
|
37
|
-
lines.push('2. `add_note
|
|
38
|
-
lines.push('3.
|
|
39
|
-
lines.push('4. `
|
|
40
|
-
lines.push('5. `complete_task` or `abandon_task` (NEVER skip this)');
|
|
42
|
+
lines.push('**š Workflow:**');
|
|
43
|
+
lines.push('1. `add_note`: "Session started: <goal>"');
|
|
44
|
+
lines.push('2. Work: use `add_commit` for commits, `add_note` for decisions');
|
|
45
|
+
lines.push('3. `add_note`: "Session end: <summary>"');
|
|
46
|
+
lines.push('4. `complete_task` or `abandon_task`');
|
|
41
47
|
return lines.join('\n');
|
|
42
48
|
}
|
|
43
49
|
/**
|
package/dist/index.js
CHANGED
|
@@ -360,6 +360,7 @@ const ContextIndexSchema = z.object({
|
|
|
360
360
|
})),
|
|
361
361
|
relevantSections: z.array(z.string()).optional(),
|
|
362
362
|
hint: z.string().optional(),
|
|
363
|
+
criticalRules: z.array(z.string()).optional(),
|
|
363
364
|
});
|
|
364
365
|
// Tool: Start task
|
|
365
366
|
server.registerTool('start_task', {
|
|
@@ -371,10 +372,8 @@ server.registerTool('start_task', {
|
|
|
371
372
|
'Project context contains architecture and patterns you MUST follow.\n\n' +
|
|
372
373
|
'**Workflow after starting:**\n' +
|
|
373
374
|
'1. Read relevant context sections (architecture, conventions, etc.)\n' +
|
|
374
|
-
'2.
|
|
375
|
-
'3.
|
|
376
|
-
'4. add_note: "Session end: <summary, next steps>"\n' +
|
|
377
|
-
'5. complete_task (done) or abandon_task (stopping)',
|
|
375
|
+
'2. Do work, log commits with add_commit\n' +
|
|
376
|
+
'3. complete_task (done) or abandon_task (stopping)',
|
|
378
377
|
inputSchema: z.object({
|
|
379
378
|
taskId: z.string(),
|
|
380
379
|
force: z.boolean().optional().describe('Take over lock from another agent'),
|
|
@@ -429,13 +428,11 @@ server.registerTool('start_task', {
|
|
|
429
428
|
// Tool: Add note
|
|
430
429
|
server.registerTool('add_note', {
|
|
431
430
|
title: 'Add Note',
|
|
432
|
-
description: 'Add
|
|
431
|
+
description: 'Add a note to a task. Use ONLY for non-obvious approach decisions and blockers.\n\n' +
|
|
433
432
|
'**When to use:**\n' +
|
|
434
|
-
'- Session start: "Session started: implementing X"\n' +
|
|
435
433
|
'- Decisions: "Decision: Using X because Y"\n' +
|
|
436
|
-
'-
|
|
437
|
-
'**
|
|
438
|
-
'**Important:** Always log session end before complete_task or abandon_task.',
|
|
434
|
+
'- Blockers: "Blocked: X needs Y before we can proceed"\n\n' +
|
|
435
|
+
'**Do NOT use for:** Session start/end ceremony, commit logging (use `add_commit`), or restating what code changes do.',
|
|
439
436
|
inputSchema: z.object({
|
|
440
437
|
taskId: z.string(),
|
|
441
438
|
note: z.string(),
|
|
@@ -568,16 +565,15 @@ const DocumentationSchema = z.object({
|
|
|
568
565
|
// Tool: Complete task
|
|
569
566
|
server.registerTool('complete_task', {
|
|
570
567
|
title: 'Complete Task',
|
|
571
|
-
description: 'Mark task done with summary. Optionally log final commits at completion.\n\n' +
|
|
568
|
+
description: 'Mark task done with a brief one-line summary. Optionally log final commits at completion.\n\n' +
|
|
572
569
|
'**Before calling:**\n' +
|
|
573
570
|
'1. Push all commits\n' +
|
|
574
|
-
'2.
|
|
575
|
-
'3. Check if project context docs need updating\n\n' +
|
|
571
|
+
'2. Check if project context docs need updating\n\n' +
|
|
576
572
|
'**Commits:** Pass commits array to log them at completion (convenience for final commits).\n\n' +
|
|
577
573
|
'Returns documentation update suggestions.',
|
|
578
574
|
inputSchema: z.object({
|
|
579
575
|
taskId: z.string(),
|
|
580
|
-
summary: z.string().describe('
|
|
576
|
+
summary: z.string().describe('Brief one-line summary of what was done'),
|
|
581
577
|
commits: z.array(z.object({
|
|
582
578
|
hash: z.string().describe('Commit hash (short or full)'),
|
|
583
579
|
message: z.string().describe('Commit message'),
|
|
@@ -606,8 +602,7 @@ server.registerTool('abandon_task', {
|
|
|
606
602
|
title: 'Abandon Task',
|
|
607
603
|
description: 'Release lock and return task to planned status. Use when stopping work.\n\n' +
|
|
608
604
|
'**Before calling:**\n' +
|
|
609
|
-
'1. Push any WIP commits\n' +
|
|
610
|
-
'2. add_note: "Session end: <progress, blockers, next steps>"\n\n' +
|
|
605
|
+
'1. Push any WIP commits\n\n' +
|
|
611
606
|
'**Summary parameter:** What was done, what remains, blockers. Helps the next agent.',
|
|
612
607
|
inputSchema: z.object({
|
|
613
608
|
taskId: z.string(),
|
|
@@ -653,7 +648,7 @@ server.registerTool('get_agent_instructions', {
|
|
|
653
648
|
openWorldHint: false,
|
|
654
649
|
},
|
|
655
650
|
}, async ({ format = 'section' }) => {
|
|
656
|
-
const lastModified = '2025-02-
|
|
651
|
+
const lastModified = '2025-02-05';
|
|
657
652
|
const section = `## Task Management with Damper MCP
|
|
658
653
|
|
|
659
654
|
> Last updated: ${lastModified}
|
|
@@ -662,7 +657,10 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
|
|
|
662
657
|
|
|
663
658
|
### At Session Start (MANDATORY)
|
|
664
659
|
1. \`get_project_context\` - **READ THIS FIRST.** Contains architecture, conventions, and critical project info.
|
|
665
|
-
2.
|
|
660
|
+
2. Load relevant architecture context using blocks (token-efficient):
|
|
661
|
+
- If TASK_CONTEXT.md has an "Available Architecture Context" section, use \`get_section_block_content(section, blockIds)\` to load only the blocks relevant to your task
|
|
662
|
+
- Otherwise, use \`get_section_blocks(section)\` to see what's available, then fetch specific blocks
|
|
663
|
+
- Only fall back to \`get_context_section\` if you need an entire section
|
|
666
664
|
3. \`list_tasks\` - Check for existing tasks to work on
|
|
667
665
|
4. If working on a task: \`start_task\` to lock it
|
|
668
666
|
|
|
@@ -671,16 +669,17 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
|
|
|
671
669
|
- \`add_note\` for decisions: "Decision: chose X because Y"
|
|
672
670
|
- \`update_subtask\` to mark subtask progress
|
|
673
671
|
- **Follow patterns from project context** - Don't reinvent; use existing conventions
|
|
672
|
+
- When you need architecture context mid-task, use \`get_section_block_content\` to load specific blocks rather than full sections
|
|
674
673
|
|
|
675
674
|
### Feedback & Changelog Integration
|
|
676
675
|
- \`link_feedback_to_task\` - Link user feedback IDs to your task (helps track what customer requests led to the feature)
|
|
677
676
|
- When completing a **public** task, it auto-adds to the project's draft changelog
|
|
678
677
|
- \`list_changelogs\` - View existing changelogs and drafts
|
|
679
|
-
- \`create_changelog\` / \`update_changelog\` - Create or edit changelog entries
|
|
678
|
+
- \`create_changelog\` / \`update_changelog\` - Create or edit changelog entries (use \`summary\` for social-friendly posts)
|
|
680
679
|
- \`add_to_changelog\` - Manually add completed tasks to a changelog
|
|
680
|
+
- \`publish_changelog\` - Publish with optional email notifications and Twitter posting
|
|
681
681
|
|
|
682
682
|
### At Session End (MANDATORY)
|
|
683
|
-
- ALWAYS call \`add_note\` with session summary before stopping
|
|
684
683
|
- ALWAYS call \`complete_task\` (if done) or \`abandon_task\` (if stopping early)
|
|
685
684
|
- NEVER leave a started task without completing or abandoning it
|
|
686
685
|
- If you learned something about the codebase, consider updating project context
|
|
@@ -813,6 +812,10 @@ server.registerTool('update_context_section', {
|
|
|
813
812
|
'names like `overview`, `conventions` are sufficient.\n\n' +
|
|
814
813
|
'**When to add tags**: Only if you have multiple related sections that should surface ' +
|
|
815
814
|
'together (e.g., all "backend" docs). Skip for simple projects.\n\n' +
|
|
815
|
+
'**criticalRules**: Short, actionable rules that agents MUST follow. These are surfaced ' +
|
|
816
|
+
'automatically in `start_task` and `get_project_context` responses without requiring ' +
|
|
817
|
+
'agents to read the full section. Use for rules that, if skipped, cause real problems ' +
|
|
818
|
+
'(e.g., "Use migrations for DB changes: bunx prisma migrate dev --name <feature>").\n\n' +
|
|
816
819
|
'ā ļø SECURITY WARNING: Do NOT include sensitive information:\n' +
|
|
817
820
|
'- API keys, secrets, passwords, tokens\n' +
|
|
818
821
|
'- Database connection strings\n' +
|
|
@@ -826,12 +829,14 @@ server.registerTool('update_context_section', {
|
|
|
826
829
|
content: z.string().describe('Markdown documentation for this section. Must NOT contain secrets, API keys, or sensitive data.'),
|
|
827
830
|
tags: z.array(z.string()).optional().describe('Categorization tags (e.g., ["backend", "critical"]). Helps match sections to task labels.'),
|
|
828
831
|
appliesTo: z.array(z.string()).optional().describe('Module names this section applies to (e.g., ["server", "dashboard"]).'),
|
|
832
|
+
criticalRules: z.array(z.string()).optional().describe('Must-follow rules shown in start_task/get_project_context (e.g., ["Use migrations: bunx prisma migrate dev --name <feature>"])'),
|
|
829
833
|
}),
|
|
830
834
|
outputSchema: z.object({
|
|
831
835
|
success: z.boolean(),
|
|
832
836
|
section: z.string(),
|
|
833
837
|
tags: z.array(z.string()).optional(),
|
|
834
838
|
appliesTo: z.array(z.string()).optional(),
|
|
839
|
+
criticalRules: z.array(z.string()).optional(),
|
|
835
840
|
warnings: z.array(z.string()).optional(),
|
|
836
841
|
}),
|
|
837
842
|
annotations: {
|
|
@@ -840,13 +845,13 @@ server.registerTool('update_context_section', {
|
|
|
840
845
|
idempotentHint: true,
|
|
841
846
|
openWorldHint: false,
|
|
842
847
|
},
|
|
843
|
-
}, async ({ section, content, tags, appliesTo }) => {
|
|
848
|
+
}, async ({ section, content, tags, appliesTo, criticalRules }) => {
|
|
844
849
|
// Encode each path segment individually but preserve slashes for hierarchical sections
|
|
845
850
|
const encodedSection = section
|
|
846
851
|
.split('/')
|
|
847
852
|
.map(part => encodeURIComponent(part))
|
|
848
853
|
.join('/');
|
|
849
|
-
const result = await api('POST', `/api/agent/context/${encodedSection}`, { content, tags, appliesTo });
|
|
854
|
+
const result = await api('POST', `/api/agent/context/${encodedSection}`, { content, tags, appliesTo, criticalRules });
|
|
850
855
|
let text = `š Updated context section: ${result.section}`;
|
|
851
856
|
if (result.tags && result.tags.length > 0) {
|
|
852
857
|
text += ` [${result.tags.join(', ')}]`;
|
|
@@ -909,6 +914,7 @@ server.registerTool('sync_project_context', {
|
|
|
909
914
|
content: z.string().describe('Markdown documentation (no secrets!)'),
|
|
910
915
|
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
911
916
|
appliesTo: z.array(z.string()).optional().describe('Module names this applies to'),
|
|
917
|
+
criticalRules: z.array(z.string()).optional().describe('Must-follow rules shown automatically'),
|
|
912
918
|
})).describe('Array of sections to upload'),
|
|
913
919
|
}),
|
|
914
920
|
outputSchema: z.object({
|
|
@@ -949,6 +955,7 @@ server.registerTool('get_project_context', {
|
|
|
949
955
|
})),
|
|
950
956
|
relevantSections: z.array(z.string()).optional(),
|
|
951
957
|
hint: z.string().optional(),
|
|
958
|
+
criticalRules: z.array(z.string()).optional(),
|
|
952
959
|
}),
|
|
953
960
|
annotations: {
|
|
954
961
|
readOnlyHint: true,
|
|
@@ -965,7 +972,16 @@ server.registerTool('get_project_context', {
|
|
|
965
972
|
structuredContent: data,
|
|
966
973
|
};
|
|
967
974
|
}
|
|
968
|
-
const lines = [
|
|
975
|
+
const lines = [];
|
|
976
|
+
// Critical rules first - most important
|
|
977
|
+
if (data.criticalRules && data.criticalRules.length > 0) {
|
|
978
|
+
lines.push('šØ **CRITICAL RULES (do not skip):**');
|
|
979
|
+
for (const rule of data.criticalRules) {
|
|
980
|
+
lines.push(`⢠${rule}`);
|
|
981
|
+
}
|
|
982
|
+
lines.push('');
|
|
983
|
+
}
|
|
984
|
+
lines.push('**Available context sections:**');
|
|
969
985
|
for (const s of data.index) {
|
|
970
986
|
const relevant = data.relevantSections?.includes(s.section) ? ' ā' : '';
|
|
971
987
|
const tags = s.tags && s.tags.length > 0 ? ` [${s.tags.join(', ')}]` : '';
|
|
@@ -979,6 +995,91 @@ server.registerTool('get_project_context', {
|
|
|
979
995
|
structuredContent: data,
|
|
980
996
|
};
|
|
981
997
|
});
|
|
998
|
+
// Tool: Get section blocks (heading index)
|
|
999
|
+
server.registerTool('get_section_blocks', {
|
|
1000
|
+
title: 'Get Section Blocks',
|
|
1001
|
+
description: 'Get a block index for a context section. Returns a list of headings (## and ###) with their ' +
|
|
1002
|
+
'character counts, without the full content. Use this to see what\'s in a section and then ' +
|
|
1003
|
+
'fetch only the blocks you need with `get_section_block_content`.\n\n' +
|
|
1004
|
+
'**When to use:**\n' +
|
|
1005
|
+
'- Before fetching a large section, check what blocks it contains\n' +
|
|
1006
|
+
'- To selectively load only relevant parts of a section\n' +
|
|
1007
|
+
'- To reduce token usage by skipping irrelevant subsections',
|
|
1008
|
+
inputSchema: z.object({
|
|
1009
|
+
section: z.string().describe('Section name (e.g., "api", "testing")'),
|
|
1010
|
+
}),
|
|
1011
|
+
outputSchema: z.object({
|
|
1012
|
+
section: z.string(),
|
|
1013
|
+
blocks: z.array(z.object({
|
|
1014
|
+
id: z.string(),
|
|
1015
|
+
heading: z.string().nullable(),
|
|
1016
|
+
level: z.number(),
|
|
1017
|
+
charCount: z.number(),
|
|
1018
|
+
})),
|
|
1019
|
+
totalChars: z.number(),
|
|
1020
|
+
}),
|
|
1021
|
+
annotations: {
|
|
1022
|
+
readOnlyHint: true,
|
|
1023
|
+
destructiveHint: false,
|
|
1024
|
+
idempotentHint: true,
|
|
1025
|
+
openWorldHint: false,
|
|
1026
|
+
},
|
|
1027
|
+
}, async ({ section }) => {
|
|
1028
|
+
const encodedSection = encodeURIComponent(section);
|
|
1029
|
+
const data = await api('GET', `/api/agent/context/${encodedSection}/blocks`);
|
|
1030
|
+
const lines = [`**Blocks in "${data.section}"** (${data.totalChars} chars total):\n`];
|
|
1031
|
+
for (const block of data.blocks) {
|
|
1032
|
+
const heading = block.heading || '(intro)';
|
|
1033
|
+
lines.push(`⢠${block.id}: ${heading} (${block.charCount} chars)`);
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1037
|
+
structuredContent: data,
|
|
1038
|
+
};
|
|
1039
|
+
});
|
|
1040
|
+
// Tool: Get section block content
|
|
1041
|
+
server.registerTool('get_section_block_content', {
|
|
1042
|
+
title: 'Get Section Block Content',
|
|
1043
|
+
description: 'Get the full content of specific blocks within a section. Use `get_section_blocks` first ' +
|
|
1044
|
+
'to see available block IDs, then fetch only the ones you need.\n\n' +
|
|
1045
|
+
'**Example workflow:**\n' +
|
|
1046
|
+
'1. `get_section_blocks("api")` ā see list of blocks\n' +
|
|
1047
|
+
'2. `get_section_block_content("api", ["authentication", "agent-endpoints"])` ā get only relevant blocks',
|
|
1048
|
+
inputSchema: z.object({
|
|
1049
|
+
section: z.string().describe('Section name (e.g., "api", "testing")'),
|
|
1050
|
+
blockIds: z.array(z.string()).describe('Block IDs to fetch (from get_section_blocks)'),
|
|
1051
|
+
}),
|
|
1052
|
+
outputSchema: z.object({
|
|
1053
|
+
section: z.string(),
|
|
1054
|
+
blocks: z.array(z.object({
|
|
1055
|
+
id: z.string(),
|
|
1056
|
+
heading: z.string().nullable(),
|
|
1057
|
+
level: z.number(),
|
|
1058
|
+
content: z.string(),
|
|
1059
|
+
charCount: z.number(),
|
|
1060
|
+
})),
|
|
1061
|
+
totalChars: z.number(),
|
|
1062
|
+
}),
|
|
1063
|
+
annotations: {
|
|
1064
|
+
readOnlyHint: true,
|
|
1065
|
+
destructiveHint: false,
|
|
1066
|
+
idempotentHint: true,
|
|
1067
|
+
openWorldHint: false,
|
|
1068
|
+
},
|
|
1069
|
+
}, async ({ section, blockIds }) => {
|
|
1070
|
+
const encodedSection = encodeURIComponent(section);
|
|
1071
|
+
const encodedBlockIds = blockIds.map(id => encodeURIComponent(id)).join(',');
|
|
1072
|
+
const data = await api('GET', `/api/agent/context/${encodedSection}/blocks/${encodedBlockIds}`);
|
|
1073
|
+
const lines = [];
|
|
1074
|
+
for (const block of data.blocks) {
|
|
1075
|
+
lines.push(block.content);
|
|
1076
|
+
lines.push('');
|
|
1077
|
+
}
|
|
1078
|
+
return {
|
|
1079
|
+
content: [{ type: 'text', text: lines.join('\n').trim() }],
|
|
1080
|
+
structuredContent: data,
|
|
1081
|
+
};
|
|
1082
|
+
});
|
|
982
1083
|
// Tool: List feedback
|
|
983
1084
|
server.registerTool('list_feedback', {
|
|
984
1085
|
title: 'List Feedback',
|
|
@@ -1490,6 +1591,7 @@ server.registerTool('create_changelog', {
|
|
|
1490
1591
|
title: z.string().describe('Changelog title (e.g., "v2.1.0" or "January 2025 Release")'),
|
|
1491
1592
|
content: z.string().optional().describe('Initial changelog content (markdown)'),
|
|
1492
1593
|
version: z.string().optional().describe('Version number'),
|
|
1594
|
+
summary: z.string().max(280).optional().describe('Short summary for social sharing and email subject (max 280 chars)'),
|
|
1493
1595
|
status: z.enum(['draft', 'published']).optional().describe('Status (default: draft)'),
|
|
1494
1596
|
}),
|
|
1495
1597
|
outputSchema: z.object({
|
|
@@ -1515,13 +1617,14 @@ server.registerTool('create_changelog', {
|
|
|
1515
1617
|
// Tool: Update changelog
|
|
1516
1618
|
server.registerTool('update_changelog', {
|
|
1517
1619
|
title: 'Update Changelog',
|
|
1518
|
-
description: 'Update a changelog entry. Can update title, content, version, or status.\n\n' +
|
|
1519
|
-
'**Publishing:**
|
|
1620
|
+
description: 'Update a changelog entry. Can update title, content, version, summary, or status.\n\n' +
|
|
1621
|
+
'**Publishing:** Use `publish_changelog` instead to publish with email and Twitter options.',
|
|
1520
1622
|
inputSchema: z.object({
|
|
1521
1623
|
changelogId: z.string().describe('Changelog ID'),
|
|
1522
1624
|
title: z.string().optional().describe('New title'),
|
|
1523
1625
|
content: z.string().optional().describe('New content (markdown)'),
|
|
1524
1626
|
version: z.string().optional().describe('Version number'),
|
|
1627
|
+
summary: z.string().max(280).optional().describe('Short summary for social sharing and email subject (max 280 chars)'),
|
|
1525
1628
|
status: z.enum(['draft', 'published']).optional().describe('Status'),
|
|
1526
1629
|
}),
|
|
1527
1630
|
outputSchema: z.object({
|
|
@@ -1589,6 +1692,80 @@ server.registerTool('add_to_changelog', {
|
|
|
1589
1692
|
structuredContent: result,
|
|
1590
1693
|
};
|
|
1591
1694
|
});
|
|
1695
|
+
// Tool: Publish changelog
|
|
1696
|
+
server.registerTool('publish_changelog', {
|
|
1697
|
+
title: 'Publish Changelog',
|
|
1698
|
+
description: 'Publish a changelog with optional email and Twitter notifications.\n\n' +
|
|
1699
|
+
'**Options:**\n' +
|
|
1700
|
+
'- `sendEmail`: Send to subscribers who opted in (default: true if subscribers exist)\n' +
|
|
1701
|
+
'- `postToTwitter`: Post to connected Twitter account (default: false)\n' +
|
|
1702
|
+
'- `summary`: Short text for email subject and tweet (max 280 chars)\n\n' +
|
|
1703
|
+
'**Example workflow:**\n' +
|
|
1704
|
+
'1. Review draft with `list_changelogs`\n' +
|
|
1705
|
+
'2. Update content with `update_changelog` if needed\n' +
|
|
1706
|
+
'3. Publish with `publish_changelog` and notification options',
|
|
1707
|
+
inputSchema: z.object({
|
|
1708
|
+
changelogId: z.string().describe('Changelog ID to publish'),
|
|
1709
|
+
title: z.string().optional().describe('Update title before publishing'),
|
|
1710
|
+
version: z.string().optional().describe('Set version before publishing'),
|
|
1711
|
+
summary: z.string().max(280).optional().describe('Summary for email subject and tweet (max 280 chars)'),
|
|
1712
|
+
sendEmail: z.boolean().optional().describe('Send email to subscribers (default: true if subscribers exist)'),
|
|
1713
|
+
postToTwitter: z.boolean().optional().describe('Post to Twitter if connected (default: false)'),
|
|
1714
|
+
}),
|
|
1715
|
+
outputSchema: z.object({
|
|
1716
|
+
id: z.string(),
|
|
1717
|
+
title: z.string(),
|
|
1718
|
+
publishedAt: z.string(),
|
|
1719
|
+
emailsSent: z.number().optional(),
|
|
1720
|
+
twitterPost: z.object({
|
|
1721
|
+
success: z.boolean(),
|
|
1722
|
+
postUrl: z.string().optional(),
|
|
1723
|
+
error: z.string().optional(),
|
|
1724
|
+
}).optional(),
|
|
1725
|
+
}),
|
|
1726
|
+
annotations: {
|
|
1727
|
+
readOnlyHint: false,
|
|
1728
|
+
destructiveHint: false,
|
|
1729
|
+
idempotentHint: false,
|
|
1730
|
+
openWorldHint: false,
|
|
1731
|
+
},
|
|
1732
|
+
}, async ({ changelogId, title, version, summary, sendEmail, postToTwitter }) => {
|
|
1733
|
+
const body = {};
|
|
1734
|
+
if (title !== undefined)
|
|
1735
|
+
body.title = title;
|
|
1736
|
+
if (version !== undefined)
|
|
1737
|
+
body.version = version;
|
|
1738
|
+
if (summary !== undefined)
|
|
1739
|
+
body.summary = summary;
|
|
1740
|
+
if (sendEmail !== undefined)
|
|
1741
|
+
body.sendEmail = sendEmail;
|
|
1742
|
+
if (postToTwitter !== undefined)
|
|
1743
|
+
body.postToTwitter = postToTwitter;
|
|
1744
|
+
const result = await api('POST', `/api/agent/changelogs/${changelogId}/publish`, body);
|
|
1745
|
+
// Build response message
|
|
1746
|
+
const parts = [`ā
Published "${result.title}"`];
|
|
1747
|
+
if (result.emailsSent && result.emailsSent > 0) {
|
|
1748
|
+
parts.push(`š§ ${result.emailsSent} email${result.emailsSent !== 1 ? 's' : ''} sent`);
|
|
1749
|
+
}
|
|
1750
|
+
if (result.twitterPost) {
|
|
1751
|
+
if (result.twitterPost.success && result.twitterPost.postUrl) {
|
|
1752
|
+
parts.push(`š¦ Posted to Twitter: ${result.twitterPost.postUrl}`);
|
|
1753
|
+
}
|
|
1754
|
+
else if (!result.twitterPost.success && result.twitterPost.error) {
|
|
1755
|
+
parts.push(`ā ļø Twitter failed: ${result.twitterPost.error}`);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return {
|
|
1759
|
+
content: [{ type: 'text', text: parts.join('\n') }],
|
|
1760
|
+
structuredContent: {
|
|
1761
|
+
id: result.id,
|
|
1762
|
+
title: result.title,
|
|
1763
|
+
publishedAt: result.publishedAt,
|
|
1764
|
+
emailsSent: result.emailsSent,
|
|
1765
|
+
twitterPost: result.twitterPost,
|
|
1766
|
+
},
|
|
1767
|
+
};
|
|
1768
|
+
});
|
|
1592
1769
|
// Start
|
|
1593
1770
|
async function main() {
|
|
1594
1771
|
const transport = new StdioServerTransport();
|