@damper/mcp 0.8.6 β 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formatters.d.ts +12 -2
- package/dist/formatters.js +28 -35
- package/dist/index.js +98 -76
- package/package.json +1 -1
package/dist/formatters.d.ts
CHANGED
|
@@ -35,6 +35,11 @@ export interface CompleteTaskResult {
|
|
|
35
35
|
reminder: string;
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
export interface ReviewTaskResult {
|
|
39
|
+
id: string;
|
|
40
|
+
status: string;
|
|
41
|
+
completionChecklist?: string[];
|
|
42
|
+
}
|
|
38
43
|
export interface AbandonTaskResult {
|
|
39
44
|
id: string;
|
|
40
45
|
status: string;
|
|
@@ -49,11 +54,16 @@ export declare function formatStartTaskResponse(result: StartTaskResult): string
|
|
|
49
54
|
* Guides the agent to propose improvements to the user with rationale.
|
|
50
55
|
*/
|
|
51
56
|
export declare function formatSelfAssessmentActions(assessment: SelfAssessment): string;
|
|
57
|
+
/**
|
|
58
|
+
* Format review_task response.
|
|
59
|
+
* Shows self-assessment follow-up actions and reminds about next steps.
|
|
60
|
+
*/
|
|
61
|
+
export declare function formatReviewTaskResponse(result: ReviewTaskResult, assessment: SelfAssessment): string;
|
|
52
62
|
/**
|
|
53
63
|
* Format complete_task response
|
|
54
64
|
*/
|
|
55
|
-
export declare function formatCompleteTaskResponse(result: CompleteTaskResult
|
|
65
|
+
export declare function formatCompleteTaskResponse(result: CompleteTaskResult): string;
|
|
56
66
|
/**
|
|
57
67
|
* Format abandon_task response
|
|
58
68
|
*/
|
|
59
|
-
export declare function formatAbandonTaskResponse(result: AbandonTaskResult, hasSummary: boolean
|
|
69
|
+
export declare function formatAbandonTaskResponse(result: AbandonTaskResult, hasSummary: boolean): string;
|
package/dist/formatters.js
CHANGED
|
@@ -52,7 +52,8 @@ export function formatStartTaskResponse(result) {
|
|
|
52
52
|
lines.push('1. `add_note`: "Session started: <goal>"');
|
|
53
53
|
lines.push('2. Work: use `add_commit` for commits, `add_note` for decisions');
|
|
54
54
|
lines.push('3. `add_note`: "Session end: <summary>"');
|
|
55
|
-
lines.push('4. `
|
|
55
|
+
lines.push('4. `review_task` (with `selfAssessment`) β required self-review before completion');
|
|
56
|
+
lines.push('5. `complete_task` (with `confirmations`) or `abandon_task`');
|
|
56
57
|
return lines.join('\n');
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
@@ -91,31 +92,38 @@ export function formatSelfAssessmentActions(assessment) {
|
|
|
91
92
|
}
|
|
92
93
|
return lines.join('\n');
|
|
93
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Format review_task response.
|
|
97
|
+
* Shows self-assessment follow-up actions and reminds about next steps.
|
|
98
|
+
*/
|
|
99
|
+
export function formatReviewTaskResponse(result, assessment) {
|
|
100
|
+
const lines = [`π Review recorded for ${result.id}.`];
|
|
101
|
+
// Self-assessment follow-up actions
|
|
102
|
+
const actions = formatSelfAssessmentActions(assessment);
|
|
103
|
+
if (actions) {
|
|
104
|
+
lines.push(actions);
|
|
105
|
+
}
|
|
106
|
+
if (!actions) {
|
|
107
|
+
lines.push('\nNo friction points reported β nice!');
|
|
108
|
+
}
|
|
109
|
+
// Remind about completion checklist
|
|
110
|
+
if (result.completionChecklist && result.completionChecklist.length > 0) {
|
|
111
|
+
lines.push('\nπ **Completion checklist (pass as `confirmations` in `complete_task`):**');
|
|
112
|
+
for (const item of result.completionChecklist) {
|
|
113
|
+
lines.push(` β‘ ${item}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push('\n**Next:** Push commits, then call `complete_task` with summary.');
|
|
117
|
+
return lines.join('\n');
|
|
118
|
+
}
|
|
94
119
|
/**
|
|
95
120
|
* Format complete_task response
|
|
96
121
|
*/
|
|
97
|
-
export function formatCompleteTaskResponse(result
|
|
122
|
+
export function formatCompleteTaskResponse(result) {
|
|
98
123
|
const statusMessage = result.status === 'done'
|
|
99
124
|
? `β
Task ${result.id} marked as done`
|
|
100
125
|
: `β
Task ${result.id} moved to review`;
|
|
101
126
|
const lines = [statusMessage];
|
|
102
|
-
// Self-assessment follow-up actions (shown first if provided)
|
|
103
|
-
if (assessment) {
|
|
104
|
-
const actions = formatSelfAssessmentActions(assessment);
|
|
105
|
-
if (actions) {
|
|
106
|
-
lines.push(actions);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (!assessment) {
|
|
110
|
-
// No assessment provided β nudge the agent to reflect
|
|
111
|
-
lines.push('\n---');
|
|
112
|
-
lines.push('**π Before you finish β quick self-assessment:**');
|
|
113
|
-
lines.push('Reflect on your session. If you encountered any of these, propose improvements to the user:');
|
|
114
|
-
lines.push('β’ Missing project docs β offer to call `update_context_section`');
|
|
115
|
-
lines.push('β’ Confusing tools β offer to call `report_issue`');
|
|
116
|
-
lines.push('β’ Spec/process gaps β offer to update docs or report');
|
|
117
|
-
lines.push('Explain briefly why each improvement would help future tasks.');
|
|
118
|
-
}
|
|
119
127
|
if (result.documentation) {
|
|
120
128
|
if (result.documentation.affectedSections?.length > 0) {
|
|
121
129
|
lines.push(`\nSections that may need updates: ${result.documentation.affectedSections.join(', ')}`);
|
|
@@ -129,26 +137,11 @@ export function formatCompleteTaskResponse(result, assessment) {
|
|
|
129
137
|
/**
|
|
130
138
|
* Format abandon_task response
|
|
131
139
|
*/
|
|
132
|
-
export function formatAbandonTaskResponse(result, hasSummary
|
|
140
|
+
export function formatAbandonTaskResponse(result, hasSummary) {
|
|
133
141
|
const lines = [`βΈοΈ Abandoned ${result.id}: ${result.message}`];
|
|
134
142
|
if (!hasSummary) {
|
|
135
143
|
lines.push('\nβ οΈ Tip: Include a summary next time for better handoff.');
|
|
136
144
|
}
|
|
137
145
|
lines.push('\nπ‘ Task returned to "planned" (from in_progress or in_review). Notes preserved for next agent.');
|
|
138
|
-
// Self-assessment follow-up actions
|
|
139
|
-
if (assessment) {
|
|
140
|
-
const actions = formatSelfAssessmentActions(assessment);
|
|
141
|
-
if (actions) {
|
|
142
|
-
lines.push(actions);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (!assessment) {
|
|
146
|
-
lines.push('\n---');
|
|
147
|
-
lines.push('**π Before you finish β quick self-assessment:**');
|
|
148
|
-
lines.push('Something caused this task to be abandoned. Propose improvements to the user:');
|
|
149
|
-
lines.push('β’ Missing project docs that would have helped β offer to call `update_context_section`');
|
|
150
|
-
lines.push('β’ Tool issues that blocked progress β offer to call `report_issue`');
|
|
151
|
-
lines.push('Explain briefly why each fix would help the next agent pick this up smoothly.');
|
|
152
|
-
}
|
|
153
146
|
return lines.join('\n');
|
|
154
147
|
}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { formatStartTaskResponse, formatCompleteTaskResponse, formatAbandonTaskResponse, } from './formatters.js';
|
|
5
|
+
import { formatStartTaskResponse, formatReviewTaskResponse, formatCompleteTaskResponse, formatAbandonTaskResponse, } from './formatters.js';
|
|
6
6
|
// Config
|
|
7
7
|
const API_KEY = process.env.DAMPER_API_KEY;
|
|
8
8
|
const API_URL = process.env.DAMPER_API_URL || 'https://api.usedamper.com';
|
|
@@ -28,6 +28,12 @@ async function api(method, path, body) {
|
|
|
28
28
|
lockErr.lockInfo = err;
|
|
29
29
|
throw lockErr;
|
|
30
30
|
}
|
|
31
|
+
// Preserve review-required info for 400 review gate
|
|
32
|
+
if (res.status === 400 && err.reviewRequired) {
|
|
33
|
+
const reviewErr = new Error(err.error || `HTTP ${res.status}`);
|
|
34
|
+
reviewErr.reviewRequired = true;
|
|
35
|
+
throw reviewErr;
|
|
36
|
+
}
|
|
31
37
|
// Preserve checklist info for 400 checklist failures
|
|
32
38
|
if (res.status === 400 && (err.missingItems || err.invalidEvidence)) {
|
|
33
39
|
const checklistErr = new Error(err.error || `HTTP ${res.status}`);
|
|
@@ -101,6 +107,7 @@ const FeedbackSummarySchema = z.object({
|
|
|
101
107
|
title: z.string(),
|
|
102
108
|
type: z.string(),
|
|
103
109
|
voterCount: z.number(),
|
|
110
|
+
isPublic: z.boolean(),
|
|
104
111
|
linkedTaskId: z.string().nullish(),
|
|
105
112
|
});
|
|
106
113
|
const FeedbackDetailSchema = z.object({
|
|
@@ -110,6 +117,8 @@ const FeedbackDetailSchema = z.object({
|
|
|
110
117
|
type: z.string(),
|
|
111
118
|
status: z.string(),
|
|
112
119
|
voteScore: z.number(),
|
|
120
|
+
isPublic: z.boolean(),
|
|
121
|
+
metadata: z.unknown().optional(),
|
|
113
122
|
linkedTaskId: z.string().nullish(),
|
|
114
123
|
voters: z.array(z.object({ email: z.string(), plan: z.string() })),
|
|
115
124
|
comments: z.array(z.object({ author: z.string(), body: z.string() })),
|
|
@@ -599,6 +608,38 @@ const SelfAssessmentSchema = z.object({
|
|
|
599
608
|
toolIssues: z.array(z.string()).optional().describe('MCP tools or workflow steps that were confusing, broken, or could be improved'),
|
|
600
609
|
processGaps: z.array(z.string()).optional().describe('Gaps in specs, checklists, or conventions that caused friction or mistakes'),
|
|
601
610
|
});
|
|
611
|
+
// Tool: Review task (self-assessment gate before completion)
|
|
612
|
+
server.registerTool('review_task', {
|
|
613
|
+
title: 'Review Task',
|
|
614
|
+
description: 'Self-review gate β MUST be called before `complete_task`.\n\n' +
|
|
615
|
+
'Reflect on your session: what project knowledge was missing, what tools were confusing, ' +
|
|
616
|
+
'what process gaps caused friction. This drives project improvements.\n\n' +
|
|
617
|
+
'**Flow:** `start_task` β work β `review_task` β `complete_task`\n\n' +
|
|
618
|
+
'If you have findings, propose improvements to the user after this call ' +
|
|
619
|
+
'(e.g., `update_context_section` for missing docs, `report_issue` for tool problems).',
|
|
620
|
+
inputSchema: z.object({
|
|
621
|
+
taskId: z.string(),
|
|
622
|
+
selfAssessment: SelfAssessmentSchema.describe('Reflect on your session: what went well, what knowledge was missing, what caused friction. ' +
|
|
623
|
+
'Be honest β this drives project improvements. Pass empty object if nothing to report.'),
|
|
624
|
+
}),
|
|
625
|
+
outputSchema: z.object({
|
|
626
|
+
id: z.string(),
|
|
627
|
+
status: z.string(),
|
|
628
|
+
completionChecklist: z.array(z.string()).optional(),
|
|
629
|
+
}),
|
|
630
|
+
annotations: {
|
|
631
|
+
readOnlyHint: false,
|
|
632
|
+
destructiveHint: false,
|
|
633
|
+
idempotentHint: true,
|
|
634
|
+
openWorldHint: false,
|
|
635
|
+
},
|
|
636
|
+
}, async ({ taskId, selfAssessment }) => {
|
|
637
|
+
const result = await api('POST', `/api/agent/tasks/${taskId}/review`, { selfAssessment });
|
|
638
|
+
return {
|
|
639
|
+
content: [{ type: 'text', text: formatReviewTaskResponse(result, selfAssessment) }],
|
|
640
|
+
structuredContent: result,
|
|
641
|
+
};
|
|
642
|
+
});
|
|
602
643
|
// Documentation reminder schema for complete_task response
|
|
603
644
|
const DocumentationSchema = z.object({
|
|
604
645
|
hasContext: z.boolean(),
|
|
@@ -610,12 +651,12 @@ server.registerTool('complete_task', {
|
|
|
610
651
|
title: 'Complete Task',
|
|
611
652
|
description: 'Mark task as ready for review (in_review). A human will verify and move it to done. ' +
|
|
612
653
|
'Pass `skipReview: true` to mark as done immediately (use when the human explicitly asks).\n\n' +
|
|
654
|
+
'**Requires `review_task` first** β completion will be rejected if you haven\'t called `review_task`.\n\n' +
|
|
613
655
|
'**Before calling:**\n' +
|
|
614
|
-
'1.
|
|
615
|
-
'2.
|
|
616
|
-
'
|
|
617
|
-
'
|
|
618
|
-
'**Completion checklist:** If the project has a completion checklist (shown in `start_task` response), ' +
|
|
656
|
+
'1. Call `review_task` (self-assessment)\n' +
|
|
657
|
+
'2. Push all commits\n' +
|
|
658
|
+
'3. Check if project context docs need updating\n\n' +
|
|
659
|
+
'**Completion checklist:** If the project has a completion checklist (shown in `start_task` and `review_task` responses), ' +
|
|
619
660
|
'you MUST pass `confirmations` β an array of `{item, evidence}` objects. Each confirmation needs the checklist item text ' +
|
|
620
661
|
'and concrete evidence proving you verified it (e.g., test output, build log snippet). ' +
|
|
621
662
|
'Empty evidence or evidence identical to the item text will be rejected.\n\n' +
|
|
@@ -624,8 +665,6 @@ server.registerTool('complete_task', {
|
|
|
624
665
|
inputSchema: z.object({
|
|
625
666
|
taskId: z.string(),
|
|
626
667
|
summary: z.string().describe('Brief one-line summary of what was done, written for end users. Focus on the outcome, not the implementation.'),
|
|
627
|
-
selfAssessment: SelfAssessmentSchema.optional().describe('Reflect on your session: what went well, what knowledge was missing, what caused friction. ' +
|
|
628
|
-
'Be honest β this drives project improvements.'),
|
|
629
668
|
commits: z.array(z.object({
|
|
630
669
|
hash: z.string().describe('Commit hash (short or full)'),
|
|
631
670
|
message: z.string().describe('Commit message'),
|
|
@@ -647,43 +686,22 @@ server.registerTool('complete_task', {
|
|
|
647
686
|
idempotentHint: true,
|
|
648
687
|
openWorldHint: false,
|
|
649
688
|
},
|
|
650
|
-
}, async ({ taskId, summary,
|
|
689
|
+
}, async ({ taskId, summary, commits, confirmations, skipReview }) => {
|
|
651
690
|
try {
|
|
652
691
|
const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary, commits, confirmations, skipReview });
|
|
653
|
-
// Log self-assessment as a structured note if provided
|
|
654
|
-
if (selfAssessment) {
|
|
655
|
-
const assessmentLines = ['Self-Assessment:'];
|
|
656
|
-
if (selfAssessment.missingContext?.length) {
|
|
657
|
-
assessmentLines.push('Missing context:');
|
|
658
|
-
for (const item of selfAssessment.missingContext)
|
|
659
|
-
assessmentLines.push(` - ${item}`);
|
|
660
|
-
}
|
|
661
|
-
if (selfAssessment.toolIssues?.length) {
|
|
662
|
-
assessmentLines.push('Tool issues:');
|
|
663
|
-
for (const item of selfAssessment.toolIssues)
|
|
664
|
-
assessmentLines.push(` - ${item}`);
|
|
665
|
-
}
|
|
666
|
-
if (selfAssessment.processGaps?.length) {
|
|
667
|
-
assessmentLines.push('Process gaps:');
|
|
668
|
-
for (const item of selfAssessment.processGaps)
|
|
669
|
-
assessmentLines.push(` - ${item}`);
|
|
670
|
-
}
|
|
671
|
-
if (assessmentLines.length > 1) {
|
|
672
|
-
try {
|
|
673
|
-
await api('POST', `/api/agent/tasks/${taskId}/notes`, { note: assessmentLines.join('\n') });
|
|
674
|
-
}
|
|
675
|
-
catch {
|
|
676
|
-
// Don't fail completion if note logging fails
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
692
|
return {
|
|
681
|
-
content: [{ type: 'text', text: formatCompleteTaskResponse(result
|
|
693
|
+
content: [{ type: 'text', text: formatCompleteTaskResponse(result) }],
|
|
682
694
|
structuredContent: result,
|
|
683
695
|
};
|
|
684
696
|
}
|
|
685
697
|
catch (err) {
|
|
686
698
|
const error = err;
|
|
699
|
+
if (error.reviewRequired) {
|
|
700
|
+
return {
|
|
701
|
+
content: [{ type: 'text', text: 'β Completion blocked β self-review required.\n\nCall `review_task` before `complete_task`. This is a required step for reflecting on your session.' }],
|
|
702
|
+
isError: true,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
687
705
|
if (error.checklistInfo) {
|
|
688
706
|
const { missingItems, invalidEvidence, checklist } = error.checklistInfo;
|
|
689
707
|
const lines = [
|
|
@@ -718,14 +736,10 @@ server.registerTool('abandon_task', {
|
|
|
718
736
|
description: 'Release lock and return task to planned status. Works for in_progress and in_review tasks.\n\n' +
|
|
719
737
|
'**Before calling:**\n' +
|
|
720
738
|
'1. Push any WIP commits\n\n' +
|
|
721
|
-
'**Summary parameter:** What was done, what remains, blockers. Helps the next agent
|
|
722
|
-
'**Self-assessment:** Especially important when abandoning β reflect on what blocked you ' +
|
|
723
|
-
'or what knowledge was missing so the next agent has a smoother experience.',
|
|
739
|
+
'**Summary parameter:** What was done, what remains, blockers. Helps the next agent.',
|
|
724
740
|
inputSchema: z.object({
|
|
725
741
|
taskId: z.string(),
|
|
726
742
|
summary: z.string().optional().describe('Handoff summary: what was done, what remains, any blockers'),
|
|
727
|
-
selfAssessment: SelfAssessmentSchema.optional().describe('Reflect on your session: what went well, what knowledge was missing, what caused friction. ' +
|
|
728
|
-
'Be honest β this drives project improvements.'),
|
|
729
743
|
}),
|
|
730
744
|
outputSchema: z.object({
|
|
731
745
|
id: z.string(),
|
|
@@ -738,37 +752,10 @@ server.registerTool('abandon_task', {
|
|
|
738
752
|
idempotentHint: true,
|
|
739
753
|
openWorldHint: false,
|
|
740
754
|
},
|
|
741
|
-
}, async ({ taskId, summary
|
|
755
|
+
}, async ({ taskId, summary }) => {
|
|
742
756
|
const result = await api('POST', `/api/agent/tasks/${taskId}/abandon`, summary ? { summary } : undefined);
|
|
743
|
-
// Log self-assessment as a structured note if provided
|
|
744
|
-
if (selfAssessment) {
|
|
745
|
-
const assessmentLines = ['Self-Assessment (abandoned):'];
|
|
746
|
-
if (selfAssessment.missingContext?.length) {
|
|
747
|
-
assessmentLines.push('Missing context:');
|
|
748
|
-
for (const item of selfAssessment.missingContext)
|
|
749
|
-
assessmentLines.push(` - ${item}`);
|
|
750
|
-
}
|
|
751
|
-
if (selfAssessment.toolIssues?.length) {
|
|
752
|
-
assessmentLines.push('Tool issues:');
|
|
753
|
-
for (const item of selfAssessment.toolIssues)
|
|
754
|
-
assessmentLines.push(` - ${item}`);
|
|
755
|
-
}
|
|
756
|
-
if (selfAssessment.processGaps?.length) {
|
|
757
|
-
assessmentLines.push('Process gaps:');
|
|
758
|
-
for (const item of selfAssessment.processGaps)
|
|
759
|
-
assessmentLines.push(` - ${item}`);
|
|
760
|
-
}
|
|
761
|
-
if (assessmentLines.length > 1) {
|
|
762
|
-
try {
|
|
763
|
-
await api('POST', `/api/agent/tasks/${taskId}/notes`, { note: assessmentLines.join('\n') });
|
|
764
|
-
}
|
|
765
|
-
catch {
|
|
766
|
-
// Don't fail abandonment if note logging fails
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
757
|
return {
|
|
771
|
-
content: [{ type: 'text', text: formatAbandonTaskResponse(result, !!summary
|
|
758
|
+
content: [{ type: 'text', text: formatAbandonTaskResponse(result, !!summary) }],
|
|
772
759
|
structuredContent: result,
|
|
773
760
|
};
|
|
774
761
|
});
|
|
@@ -861,7 +848,7 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
|
|
|
861
848
|
2. **Abandon and hand off** - Call \`abandon_task\` with a detailed handoff summary for the next agent.
|
|
862
849
|
- NEVER leave a started task without completing or abandoning it
|
|
863
850
|
- If the project has a **completion checklist** (shown in \`start_task\` response), you MUST pass all items as \`confirmations\` when calling \`complete_task\`
|
|
864
|
-
- **Self-
|
|
851
|
+
- **Self-review (required):** Call \`review_task\` before \`complete_task\`. Pass \`selfAssessment\` reflecting on:
|
|
865
852
|
- \`missingContext\` β What project knowledge was missing or wrong?
|
|
866
853
|
- \`toolIssues\` β Which tools or workflow steps were confusing or broken?
|
|
867
854
|
- \`processGaps\` β What specs or conventions were missing?
|
|
@@ -1334,7 +1321,8 @@ server.registerTool('list_feedback', {
|
|
|
1334
1321
|
}
|
|
1335
1322
|
const lines = data.feedback.map((f) => {
|
|
1336
1323
|
const link = f.linkedTaskId ? ` β ${f.linkedTaskId}` : '';
|
|
1337
|
-
|
|
1324
|
+
const hidden = !f.isPublic ? ' [hidden]' : '';
|
|
1325
|
+
return `β’ ${f.id}: ${f.title} [${f.type}] (${f.voterCount} votes)${hidden}${link}`;
|
|
1338
1326
|
});
|
|
1339
1327
|
return {
|
|
1340
1328
|
content: [{ type: 'text', text: `Feedback:\n${lines.join('\n')}` }],
|
|
@@ -1364,6 +1352,33 @@ server.registerTool('get_feedback', {
|
|
|
1364
1352
|
f.linkedTaskId ? `Linked: ${f.linkedTaskId}` : '',
|
|
1365
1353
|
`\n${f.description}`,
|
|
1366
1354
|
].filter(Boolean);
|
|
1355
|
+
if (f.metadata && typeof f.metadata === 'object' && Object.keys(f.metadata).length > 0) {
|
|
1356
|
+
const meta = f.metadata;
|
|
1357
|
+
parts.push(`\n## Debug Info`);
|
|
1358
|
+
if (meta.url)
|
|
1359
|
+
parts.push(`URL: ${meta.url}`);
|
|
1360
|
+
if (meta.userAgent)
|
|
1361
|
+
parts.push(`User Agent: ${meta.userAgent}`);
|
|
1362
|
+
if (meta.viewport)
|
|
1363
|
+
parts.push(`Viewport: ${JSON.stringify(meta.viewport)}`);
|
|
1364
|
+
if (meta.screen)
|
|
1365
|
+
parts.push(`Screen: ${JSON.stringify(meta.screen)}`);
|
|
1366
|
+
if (meta.language)
|
|
1367
|
+
parts.push(`Language: ${meta.language}`);
|
|
1368
|
+
if (meta.referrer)
|
|
1369
|
+
parts.push(`Referrer: ${meta.referrer}`);
|
|
1370
|
+
if (meta.timeOnPage)
|
|
1371
|
+
parts.push(`Time on page: ${meta.timeOnPage}s`);
|
|
1372
|
+
if (meta.debug) {
|
|
1373
|
+
const debug = meta.debug;
|
|
1374
|
+
if (Array.isArray(debug.errors) && debug.errors.length)
|
|
1375
|
+
parts.push(`\n### Errors\n${debug.errors.map((e) => `- ${typeof e === 'string' ? e : JSON.stringify(e)}`).join('\n')}`);
|
|
1376
|
+
if (Array.isArray(debug.networkFailures) && debug.networkFailures.length)
|
|
1377
|
+
parts.push(`\n### Network Failures\n${debug.networkFailures.map((n) => `- ${typeof n === 'string' ? n : JSON.stringify(n)}`).join('\n')}`);
|
|
1378
|
+
if (Array.isArray(debug.breadcrumbs) && debug.breadcrumbs.length)
|
|
1379
|
+
parts.push(`\n### Breadcrumbs\n${debug.breadcrumbs.map((b) => `- ${typeof b === 'string' ? b : JSON.stringify(b)}`).join('\n')}`);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1367
1382
|
if (f.voters.length) {
|
|
1368
1383
|
parts.push(`\n## Voters (${f.voters.length})`);
|
|
1369
1384
|
f.voters.slice(0, 5).forEach((v) => parts.push(`- ${v.email} (${v.plan})`));
|
|
@@ -1384,12 +1399,14 @@ server.registerTool('get_feedback', {
|
|
|
1384
1399
|
// Tool: Update feedback
|
|
1385
1400
|
server.registerTool('update_feedback', {
|
|
1386
1401
|
title: 'Update Feedback',
|
|
1387
|
-
description: 'Update feedback status. Use to triage, close, or
|
|
1402
|
+
description: 'Update feedback status or visibility. Use to triage, close, mark as done, or hide from public page.',
|
|
1388
1403
|
inputSchema: z.object({
|
|
1389
1404
|
feedbackId: z.string().describe('Feedback ID'),
|
|
1390
1405
|
status: z
|
|
1391
1406
|
.enum(['new', 'under_review', 'planned', 'in_progress', 'done', 'closed'])
|
|
1407
|
+
.optional()
|
|
1392
1408
|
.describe('New status'),
|
|
1409
|
+
isPublic: z.boolean().optional().describe('Whether feedback is visible on public page'),
|
|
1393
1410
|
}),
|
|
1394
1411
|
outputSchema: z.object({
|
|
1395
1412
|
id: z.string(),
|
|
@@ -1403,8 +1420,13 @@ server.registerTool('update_feedback', {
|
|
|
1403
1420
|
idempotentHint: true,
|
|
1404
1421
|
openWorldHint: false,
|
|
1405
1422
|
},
|
|
1406
|
-
}, async ({ feedbackId, status }) => {
|
|
1407
|
-
const
|
|
1423
|
+
}, async ({ feedbackId, status, isPublic }) => {
|
|
1424
|
+
const body = {};
|
|
1425
|
+
if (status)
|
|
1426
|
+
body.status = status;
|
|
1427
|
+
if (isPublic !== undefined)
|
|
1428
|
+
body.isPublic = isPublic;
|
|
1429
|
+
const result = await api('PATCH', `/api/agent/feedback/${feedbackId}`, body);
|
|
1408
1430
|
return {
|
|
1409
1431
|
content: [
|
|
1410
1432
|
{
|