@vibetasks/mcp-server 0.5.2 → 0.5.4
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/index.js +569 -158
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -12,6 +12,12 @@ import {
|
|
|
12
12
|
import { AuthManager, TaskOperations } from "@vibetasks/core";
|
|
13
13
|
|
|
14
14
|
// src/tools/index.ts
|
|
15
|
+
import {
|
|
16
|
+
parseError,
|
|
17
|
+
generateErrorTaskTitle,
|
|
18
|
+
detectProjectFromError,
|
|
19
|
+
containsError
|
|
20
|
+
} from "@vibetasks/shared";
|
|
15
21
|
import { z } from "zod";
|
|
16
22
|
function setupTools(taskOps) {
|
|
17
23
|
return [
|
|
@@ -124,7 +130,8 @@ function setupTools(taskOps) {
|
|
|
124
130
|
inputSchema: z.object({
|
|
125
131
|
task_id: z.string().describe("Task ID"),
|
|
126
132
|
title: z.string().optional().describe("New title"),
|
|
127
|
-
notes: z.string().optional().describe("New notes"),
|
|
133
|
+
notes: z.string().optional().describe("New notes (user-facing description)"),
|
|
134
|
+
context_notes: z.string().optional().describe("AI context notes - progress, blockers, next steps"),
|
|
128
135
|
due_date: z.string().optional().describe("New due date (ISO 8601)"),
|
|
129
136
|
priority: z.enum(["none", "low", "medium", "high"]).optional().describe("New priority"),
|
|
130
137
|
completed: z.boolean().optional().describe("Completion status")
|
|
@@ -133,6 +140,7 @@ function setupTools(taskOps) {
|
|
|
133
140
|
const updates = {};
|
|
134
141
|
if (args.title !== void 0) updates.title = args.title;
|
|
135
142
|
if (args.notes !== void 0) updates.notes = args.notes;
|
|
143
|
+
if (args.context_notes !== void 0) updates.context_notes = args.context_notes;
|
|
136
144
|
if (args.due_date !== void 0) updates.due_date = args.due_date;
|
|
137
145
|
if (args.priority !== void 0) updates.priority = args.priority;
|
|
138
146
|
if (args.completed !== void 0) updates.completed = args.completed;
|
|
@@ -148,7 +156,8 @@ function setupTools(taskOps) {
|
|
|
148
156
|
id: task.id,
|
|
149
157
|
title: task.title,
|
|
150
158
|
completed: task.completed,
|
|
151
|
-
priority: task.priority
|
|
159
|
+
priority: task.priority,
|
|
160
|
+
context_notes: task.context_notes
|
|
152
161
|
}
|
|
153
162
|
},
|
|
154
163
|
null,
|
|
@@ -162,11 +171,15 @@ function setupTools(taskOps) {
|
|
|
162
171
|
// complete_task
|
|
163
172
|
{
|
|
164
173
|
name: "complete_task",
|
|
165
|
-
description: "Mark a task as complete",
|
|
174
|
+
description: "Mark a task as complete, optionally adding completion notes",
|
|
166
175
|
inputSchema: z.object({
|
|
167
|
-
task_id: z.string().describe("Task ID")
|
|
176
|
+
task_id: z.string().describe("Task ID"),
|
|
177
|
+
context_notes: z.string().optional().describe("Completion notes - what was done, any final notes")
|
|
168
178
|
}),
|
|
169
179
|
handler: async (args, taskOps2) => {
|
|
180
|
+
if (args.context_notes) {
|
|
181
|
+
await taskOps2.updateTask(args.task_id, { context_notes: args.context_notes });
|
|
182
|
+
}
|
|
170
183
|
const task = await taskOps2.completeTask(args.task_id);
|
|
171
184
|
return {
|
|
172
185
|
content: [
|
|
@@ -179,7 +192,8 @@ function setupTools(taskOps) {
|
|
|
179
192
|
id: task.id,
|
|
180
193
|
title: task.title,
|
|
181
194
|
completed: true,
|
|
182
|
-
completed_at: task.completed_at
|
|
195
|
+
completed_at: task.completed_at,
|
|
196
|
+
context_notes: args.context_notes || task.context_notes
|
|
183
197
|
}
|
|
184
198
|
},
|
|
185
199
|
null,
|
|
@@ -670,11 +684,28 @@ Generated by TaskFlow MCP Server`;
|
|
|
670
684
|
description: "Parse error text from clipboard, terminal, or direct paste. Extracts structured information from common error formats (Node.js, Python, Expo, webpack, TypeScript, etc.) for AI consumption.",
|
|
671
685
|
inputSchema: z.object({
|
|
672
686
|
error_text: z.string().describe("The raw error text to parse"),
|
|
673
|
-
source: z.enum(["terminal", "browser", "
|
|
687
|
+
source: z.enum(["terminal", "browser", "paste", "clipboard", "sentry", "ci"]).default("paste").describe("Source of the error (helps with parsing)")
|
|
674
688
|
}),
|
|
675
689
|
handler: async (args, _taskOps) => {
|
|
676
690
|
const { error_text, source } = args;
|
|
677
691
|
const parsed = parseError(error_text, source);
|
|
692
|
+
if (!parsed) {
|
|
693
|
+
return {
|
|
694
|
+
content: [
|
|
695
|
+
{
|
|
696
|
+
type: "text",
|
|
697
|
+
text: JSON.stringify(
|
|
698
|
+
{
|
|
699
|
+
success: false,
|
|
700
|
+
message: "Could not parse error. Text does not appear to be an error."
|
|
701
|
+
},
|
|
702
|
+
null,
|
|
703
|
+
2
|
|
704
|
+
)
|
|
705
|
+
}
|
|
706
|
+
]
|
|
707
|
+
};
|
|
708
|
+
}
|
|
678
709
|
return {
|
|
679
710
|
content: [
|
|
680
711
|
{
|
|
@@ -682,7 +713,9 @@ Generated by TaskFlow MCP Server`;
|
|
|
682
713
|
text: JSON.stringify(
|
|
683
714
|
{
|
|
684
715
|
success: true,
|
|
685
|
-
|
|
716
|
+
error_context: parsed,
|
|
717
|
+
suggested_title: generateErrorTaskTitle(parsed),
|
|
718
|
+
detected_project: detectProjectFromError(parsed)
|
|
686
719
|
},
|
|
687
720
|
null,
|
|
688
721
|
2
|
|
@@ -702,7 +735,27 @@ Generated by TaskFlow MCP Server`;
|
|
|
702
735
|
}),
|
|
703
736
|
handler: async (args, _taskOps) => {
|
|
704
737
|
const { log_text, max_lines = 100 } = args;
|
|
705
|
-
|
|
738
|
+
if (!containsError(log_text)) {
|
|
739
|
+
return {
|
|
740
|
+
content: [
|
|
741
|
+
{
|
|
742
|
+
type: "text",
|
|
743
|
+
text: JSON.stringify(
|
|
744
|
+
{
|
|
745
|
+
success: true,
|
|
746
|
+
summary: "No errors detected in log output.",
|
|
747
|
+
error_count: 0,
|
|
748
|
+
unique_errors: 0,
|
|
749
|
+
grouped_errors: []
|
|
750
|
+
},
|
|
751
|
+
null,
|
|
752
|
+
2
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
]
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
const result = summarizeErrorsLocal(log_text, max_lines);
|
|
706
759
|
return {
|
|
707
760
|
content: [
|
|
708
761
|
{
|
|
@@ -733,7 +786,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
733
786
|
}),
|
|
734
787
|
handler: async (args, taskOps2) => {
|
|
735
788
|
const task = await taskOps2.getTask(args.task_id);
|
|
736
|
-
const subtasks = task.subtasks_json ||
|
|
789
|
+
const subtasks = task.subtasks_json || [];
|
|
737
790
|
const subtaskIndex = subtasks.findIndex((st) => st.id === args.subtask_id);
|
|
738
791
|
if (subtaskIndex === -1) {
|
|
739
792
|
return {
|
|
@@ -780,7 +833,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
780
833
|
}),
|
|
781
834
|
handler: async (args, taskOps2) => {
|
|
782
835
|
const task = await taskOps2.getTask(args.task_id);
|
|
783
|
-
const subtasks = task.subtasks_json ||
|
|
836
|
+
const subtasks = task.subtasks_json || [];
|
|
784
837
|
const newSubtask = {
|
|
785
838
|
id: crypto.randomUUID(),
|
|
786
839
|
title: args.title,
|
|
@@ -905,161 +958,352 @@ Generated by TaskFlow MCP Server`;
|
|
|
905
958
|
]
|
|
906
959
|
};
|
|
907
960
|
}
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
961
|
+
},
|
|
962
|
+
// report_error - AI can report errors it encounters
|
|
963
|
+
{
|
|
964
|
+
name: "report_error",
|
|
965
|
+
description: "Report an error you encountered while working. Creates a high-priority task to track and fix it. Use this when you hit a build error, runtime error, or any issue that blocks progress.",
|
|
966
|
+
inputSchema: z.object({
|
|
967
|
+
error_text: z.string().describe("The full error message including stack trace"),
|
|
968
|
+
context: z.string().optional().describe("What you were trying to do when this error occurred"),
|
|
969
|
+
file_path: z.string().optional().describe("File path where the error occurred (if known)"),
|
|
970
|
+
project: z.string().optional().describe("Project name (auto-detected from file path if not provided)")
|
|
971
|
+
}),
|
|
972
|
+
handler: async (args, taskOps2) => {
|
|
973
|
+
const errorText = args.error_text;
|
|
974
|
+
const errorContext = parseError(errorText, "terminal");
|
|
975
|
+
let title = errorContext ? generateErrorTaskTitle(errorContext) : "Error to fix";
|
|
976
|
+
let project = args.project;
|
|
977
|
+
if (!project && errorContext) {
|
|
978
|
+
project = detectProjectFromError(errorContext);
|
|
979
|
+
}
|
|
980
|
+
if (!project && args.file_path) {
|
|
981
|
+
const fileMatch = args.file_path.match(/(?:apps|packages)\/([^\/\s]+)\//);
|
|
982
|
+
if (fileMatch) project = fileMatch[1];
|
|
983
|
+
}
|
|
984
|
+
let notes = "";
|
|
985
|
+
if (args.context) {
|
|
986
|
+
notes += `**Context:** ${args.context}
|
|
987
|
+
|
|
988
|
+
`;
|
|
989
|
+
}
|
|
990
|
+
if (args.file_path || errorContext?.file_path) {
|
|
991
|
+
notes += `**File:** \`${args.file_path || errorContext?.file_path}\``;
|
|
992
|
+
if (errorContext?.line_number) {
|
|
993
|
+
notes += `:${errorContext.line_number}`;
|
|
994
|
+
if (errorContext.column) notes += `:${errorContext.column}`;
|
|
995
|
+
}
|
|
996
|
+
notes += "\n\n";
|
|
997
|
+
}
|
|
998
|
+
if (errorContext?.suggestion) {
|
|
999
|
+
notes += `**Suggestion:** ${errorContext.suggestion}
|
|
1000
|
+
|
|
1001
|
+
`;
|
|
1002
|
+
}
|
|
1003
|
+
notes += `**Error:**
|
|
1004
|
+
\`\`\`
|
|
1005
|
+
${errorText}
|
|
1006
|
+
\`\`\``;
|
|
1007
|
+
const task = await taskOps2.createTask({
|
|
1008
|
+
title: title.length > 80 ? title.slice(0, 77) + "..." : title,
|
|
1009
|
+
notes,
|
|
1010
|
+
priority: "high",
|
|
1011
|
+
status: "todo",
|
|
1012
|
+
project_tag: project,
|
|
1013
|
+
created_by: "ai",
|
|
1014
|
+
error_context: errorContext || void 0
|
|
1015
|
+
});
|
|
1016
|
+
return {
|
|
1017
|
+
content: [
|
|
1018
|
+
{
|
|
1019
|
+
type: "text",
|
|
1020
|
+
text: JSON.stringify(
|
|
1021
|
+
{
|
|
1022
|
+
success: true,
|
|
1023
|
+
message: "Error reported and task created",
|
|
1024
|
+
task: {
|
|
1025
|
+
id: task.id,
|
|
1026
|
+
title: task.title,
|
|
1027
|
+
priority: task.priority,
|
|
1028
|
+
project: task.project_tag,
|
|
1029
|
+
error_category: errorContext?.category
|
|
1030
|
+
},
|
|
1031
|
+
parsed: errorContext ? {
|
|
1032
|
+
category: errorContext.category,
|
|
1033
|
+
error_type: errorContext.error_type,
|
|
1034
|
+
file: errorContext.file_path,
|
|
1035
|
+
line: errorContext.line_number
|
|
1036
|
+
} : null
|
|
1037
|
+
},
|
|
1038
|
+
null,
|
|
1039
|
+
2
|
|
1040
|
+
)
|
|
1041
|
+
}
|
|
1042
|
+
]
|
|
1043
|
+
};
|
|
978
1044
|
}
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1045
|
+
},
|
|
1046
|
+
// get_errors - DEPRECATED: Use get_inbox(source: 'errors') instead
|
|
1047
|
+
{
|
|
1048
|
+
name: "get_errors",
|
|
1049
|
+
description: '[DEPRECATED - use get_inbox with source="errors" instead] Get current errors/bugs. This is a legacy tool - prefer get_inbox for unified inbox access.',
|
|
1050
|
+
inputSchema: z.object({
|
|
1051
|
+
priority: z.enum(["critical", "high", "all"]).default("all").describe("Filter by priority"),
|
|
1052
|
+
source: z.enum(["sentry", "ci", "manual", "all"]).default("all").describe("Filter by source"),
|
|
1053
|
+
unacknowledged_only: z.boolean().default(true).describe("Only show errors you haven't acknowledged yet"),
|
|
1054
|
+
limit: z.number().default(5).describe("Max errors to return")
|
|
1055
|
+
}),
|
|
1056
|
+
handler: async (args, taskOps2) => {
|
|
1057
|
+
const sourceTypes = args.source === "all" ? ["sentry", "ci"] : args.source === "sentry" ? ["sentry"] : args.source === "ci" ? ["ci"] : [];
|
|
1058
|
+
const inboxItems = await taskOps2.getInboxItems({
|
|
1059
|
+
source_type: sourceTypes.length > 0 ? sourceTypes : void 0,
|
|
1060
|
+
priority: args.priority === "all" ? void 0 : args.priority,
|
|
1061
|
+
unacknowledged_only: args.unacknowledged_only,
|
|
1062
|
+
limit: args.limit
|
|
1063
|
+
});
|
|
1064
|
+
return {
|
|
1065
|
+
content: [
|
|
1066
|
+
{
|
|
1067
|
+
type: "text",
|
|
1068
|
+
text: JSON.stringify(
|
|
1069
|
+
{
|
|
1070
|
+
deprecated: true,
|
|
1071
|
+
message: 'This tool is deprecated. Use get_inbox(source: "errors") for better results.',
|
|
1072
|
+
errors: inboxItems.map((t) => ({
|
|
1073
|
+
id: t.id,
|
|
1074
|
+
title: t.title,
|
|
1075
|
+
project: t.project_tag,
|
|
1076
|
+
status: t.status,
|
|
1077
|
+
source: t.source_type || (t.sentry_issue_id ? "sentry" : "manual"),
|
|
1078
|
+
created: t.created_at,
|
|
1079
|
+
error_category: t.error_context?.category
|
|
1080
|
+
})),
|
|
1081
|
+
total_matching: inboxItems.length,
|
|
1082
|
+
showing: inboxItems.length,
|
|
1083
|
+
hint: 'Migrate to get_inbox(source: "errors") for unified inbox access.'
|
|
1084
|
+
},
|
|
1085
|
+
null,
|
|
1086
|
+
2
|
|
1087
|
+
)
|
|
1088
|
+
}
|
|
1089
|
+
]
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
},
|
|
1093
|
+
// get_error_details - Full context for specific error
|
|
1094
|
+
{
|
|
1095
|
+
name: "get_error_details",
|
|
1096
|
+
description: "Get full details for a specific error including parsed error context, notes, stack trace, and file context. Use after get_inbox to dive into a specific issue.",
|
|
1097
|
+
inputSchema: z.object({
|
|
1098
|
+
error_id: z.string().describe("Error task ID")
|
|
1099
|
+
}),
|
|
1100
|
+
handler: async (args, taskOps2) => {
|
|
1101
|
+
const task = await taskOps2.getTask(args.error_id);
|
|
1102
|
+
const errorContext = task.error_context;
|
|
1103
|
+
return {
|
|
1104
|
+
content: [
|
|
1105
|
+
{
|
|
1106
|
+
type: "text",
|
|
1107
|
+
text: JSON.stringify(
|
|
1108
|
+
{
|
|
1109
|
+
id: task.id,
|
|
1110
|
+
title: task.title,
|
|
1111
|
+
description: task.description,
|
|
1112
|
+
notes: task.notes,
|
|
1113
|
+
project: task.project_tag,
|
|
1114
|
+
status: task.status,
|
|
1115
|
+
priority: task.priority,
|
|
1116
|
+
created_at: task.created_at,
|
|
1117
|
+
created_by: task.created_by,
|
|
1118
|
+
context_notes: task.context_notes,
|
|
1119
|
+
// Parsed error context from shared parser
|
|
1120
|
+
error_context: errorContext ? {
|
|
1121
|
+
category: errorContext.category,
|
|
1122
|
+
error_type: errorContext.error_type,
|
|
1123
|
+
message: errorContext.message,
|
|
1124
|
+
file_path: errorContext.file_path,
|
|
1125
|
+
line_number: errorContext.line_number,
|
|
1126
|
+
column: errorContext.column,
|
|
1127
|
+
stack_trace: errorContext.stack_trace,
|
|
1128
|
+
suggestion: errorContext.suggestion,
|
|
1129
|
+
captured_at: errorContext.captured_at,
|
|
1130
|
+
capture_source: errorContext.capture_source
|
|
1131
|
+
} : null,
|
|
1132
|
+
// Sentry-specific
|
|
1133
|
+
sentry: task.sentry_issue_id ? {
|
|
1134
|
+
issue_id: task.sentry_issue_id,
|
|
1135
|
+
project: task.sentry_project,
|
|
1136
|
+
error_count: task.sentry_error_count,
|
|
1137
|
+
first_seen: task.sentry_first_seen,
|
|
1138
|
+
last_seen: task.sentry_last_seen,
|
|
1139
|
+
level: task.sentry_level,
|
|
1140
|
+
url: task.sentry_url
|
|
1141
|
+
} : null,
|
|
1142
|
+
subtasks: task.subtasks_json || [],
|
|
1143
|
+
tags: task.tags?.map((t) => t.name) || []
|
|
1144
|
+
},
|
|
1145
|
+
null,
|
|
1146
|
+
2
|
|
1147
|
+
)
|
|
1148
|
+
}
|
|
1149
|
+
]
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
},
|
|
1153
|
+
// acknowledge_error - Mark error as seen/addressed
|
|
1154
|
+
{
|
|
1155
|
+
name: "acknowledge_error",
|
|
1156
|
+
description: "Mark an error as acknowledged so it doesn't keep appearing. Use this after you've investigated or fixed an error.",
|
|
1157
|
+
inputSchema: z.object({
|
|
1158
|
+
error_id: z.string().describe("Error task ID"),
|
|
1159
|
+
action: z.enum(["investigating", "fixed", "wont_fix", "need_info", "delegated"]).describe("What action was taken"),
|
|
1160
|
+
notes: z.string().optional().describe("Optional notes about what was done")
|
|
1161
|
+
}),
|
|
1162
|
+
handler: async (args, taskOps2) => {
|
|
1163
|
+
const task = await taskOps2.getTask(args.error_id);
|
|
1164
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1165
|
+
const ackNote = `[AI_ACKNOWLEDGED:${args.action}:${timestamp}]`;
|
|
1166
|
+
const existingNotes = task.context_notes || "";
|
|
1167
|
+
const newNotes = args.notes ? `${existingNotes}
|
|
1168
|
+
|
|
1169
|
+
${ackNote}
|
|
1170
|
+
${args.notes}` : `${existingNotes}
|
|
1171
|
+
|
|
1172
|
+
${ackNote}`;
|
|
1173
|
+
let newStatus = task.status;
|
|
1174
|
+
if (args.action === "fixed") {
|
|
1175
|
+
newStatus = "done";
|
|
1176
|
+
} else if (args.action === "investigating") {
|
|
1177
|
+
newStatus = "vibing";
|
|
992
1178
|
}
|
|
1179
|
+
await taskOps2.updateTask(args.error_id, {
|
|
1180
|
+
context_notes: newNotes.trim(),
|
|
1181
|
+
status: newStatus
|
|
1182
|
+
});
|
|
1183
|
+
return {
|
|
1184
|
+
content: [
|
|
1185
|
+
{
|
|
1186
|
+
type: "text",
|
|
1187
|
+
text: JSON.stringify(
|
|
1188
|
+
{
|
|
1189
|
+
success: true,
|
|
1190
|
+
error_id: args.error_id,
|
|
1191
|
+
action: args.action,
|
|
1192
|
+
new_status: newStatus,
|
|
1193
|
+
message: `Error acknowledged as '${args.action}'. It won't appear in unacknowledged lists.`
|
|
1194
|
+
},
|
|
1195
|
+
null,
|
|
1196
|
+
2
|
|
1197
|
+
)
|
|
1198
|
+
}
|
|
1199
|
+
]
|
|
1200
|
+
};
|
|
993
1201
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1202
|
+
},
|
|
1203
|
+
// get_inbox - Unified inbox for all external sources
|
|
1204
|
+
{
|
|
1205
|
+
name: "get_inbox",
|
|
1206
|
+
description: "Get inbox items from all external sources (Sentry errors, GitHub issues, email, Slack, CI failures). Use this to see what needs attention.",
|
|
1207
|
+
inputSchema: z.object({
|
|
1208
|
+
source: z.enum(["all", "sentry", "ci", "github", "email", "slack", "errors", "feedback"]).default("all").describe("Filter by source: all, sentry, ci, github, email, slack, errors (sentry+ci), feedback (github+email+slack)"),
|
|
1209
|
+
priority: z.enum(["all", "high", "medium", "low"]).default("all").describe("Filter by priority"),
|
|
1210
|
+
unacknowledged_only: z.boolean().default(true).describe("Only show items not yet acknowledged"),
|
|
1211
|
+
limit: z.number().default(10).describe("Maximum items to return")
|
|
1212
|
+
}),
|
|
1213
|
+
handler: async (args, taskOps2) => {
|
|
1214
|
+
let sourceTypes;
|
|
1215
|
+
switch (args.source) {
|
|
1216
|
+
case "sentry":
|
|
1217
|
+
sourceTypes = ["sentry"];
|
|
1218
|
+
break;
|
|
1219
|
+
case "ci":
|
|
1220
|
+
sourceTypes = ["ci"];
|
|
1221
|
+
break;
|
|
1222
|
+
case "github":
|
|
1223
|
+
sourceTypes = ["github_bug", "github_feedback"];
|
|
1224
|
+
break;
|
|
1225
|
+
case "email":
|
|
1226
|
+
sourceTypes = ["email_bug", "email_feedback"];
|
|
1227
|
+
break;
|
|
1228
|
+
case "slack":
|
|
1229
|
+
sourceTypes = ["slack_bug", "slack_feedback"];
|
|
1230
|
+
break;
|
|
1231
|
+
case "errors":
|
|
1232
|
+
sourceTypes = ["sentry", "ci"];
|
|
1233
|
+
break;
|
|
1234
|
+
case "feedback":
|
|
1235
|
+
sourceTypes = ["github_bug", "github_feedback", "email_bug", "email_feedback", "slack_bug", "slack_feedback"];
|
|
1236
|
+
break;
|
|
1237
|
+
default:
|
|
1238
|
+
sourceTypes = void 0;
|
|
1239
|
+
}
|
|
1240
|
+
const inboxItems = await taskOps2.getInboxItems({
|
|
1241
|
+
source_type: sourceTypes,
|
|
1242
|
+
priority: args.priority === "all" ? void 0 : args.priority,
|
|
1243
|
+
unacknowledged_only: args.unacknowledged_only,
|
|
1244
|
+
limit: args.limit
|
|
1245
|
+
});
|
|
1246
|
+
return {
|
|
1247
|
+
content: [
|
|
1248
|
+
{
|
|
1249
|
+
type: "text",
|
|
1250
|
+
text: JSON.stringify(
|
|
1251
|
+
{
|
|
1252
|
+
success: true,
|
|
1253
|
+
inbox_items: inboxItems.map((t) => ({
|
|
1254
|
+
id: t.id,
|
|
1255
|
+
title: t.title,
|
|
1256
|
+
source: t.source_type || (t.sentry_issue_id ? "sentry" : t.github_issue_id ? "github" : t.tags?.some((tag) => tag.name === "ci") ? "ci" : "manual"),
|
|
1257
|
+
priority: t.priority,
|
|
1258
|
+
status: t.status,
|
|
1259
|
+
project: t.project_tag,
|
|
1260
|
+
created_at: t.created_at,
|
|
1261
|
+
acknowledged: t.context_notes?.includes("[AI_ACKNOWLEDGED]") || false,
|
|
1262
|
+
// Include relevant integration details
|
|
1263
|
+
sentry_count: t.sentry_error_count,
|
|
1264
|
+
github_issue: t.github_issue_number,
|
|
1265
|
+
github_repo: t.github_repo
|
|
1266
|
+
})),
|
|
1267
|
+
count: inboxItems.length,
|
|
1268
|
+
filter: { source: args.source, priority: args.priority, unacknowledged_only: args.unacknowledged_only },
|
|
1269
|
+
hint: inboxItems.length > 0 ? "Use get_error_details(id) for full context. Use acknowledge_error(id) when addressed." : "Inbox is empty!"
|
|
1270
|
+
},
|
|
1271
|
+
null,
|
|
1272
|
+
2
|
|
1273
|
+
)
|
|
1274
|
+
}
|
|
1275
|
+
]
|
|
1276
|
+
};
|
|
1005
1277
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1278
|
+
},
|
|
1279
|
+
// get_inbox_stats - Quick summary of inbox status
|
|
1280
|
+
{
|
|
1281
|
+
name: "get_inbox_stats",
|
|
1282
|
+
description: "Get a quick summary of inbox status - counts by source and priority. Check this first to understand what needs attention.",
|
|
1283
|
+
inputSchema: z.object({}),
|
|
1284
|
+
handler: async (_args, taskOps2) => {
|
|
1285
|
+
const stats = await taskOps2.getInboxStats();
|
|
1286
|
+
return {
|
|
1287
|
+
content: [
|
|
1288
|
+
{
|
|
1289
|
+
type: "text",
|
|
1290
|
+
text: JSON.stringify(
|
|
1291
|
+
{
|
|
1292
|
+
success: true,
|
|
1293
|
+
...stats,
|
|
1294
|
+
hint: stats.needs_attention ? "Use get_inbox() to see items that need attention." : "Inbox is under control!"
|
|
1295
|
+
},
|
|
1296
|
+
null,
|
|
1297
|
+
2
|
|
1298
|
+
)
|
|
1299
|
+
}
|
|
1300
|
+
]
|
|
1301
|
+
};
|
|
1022
1302
|
}
|
|
1023
1303
|
}
|
|
1024
|
-
if (errorType !== "Unknown" && !message && trimmedLine && !trimmedLine.startsWith("at ")) {
|
|
1025
|
-
message = trimmedLine;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
if (!message) {
|
|
1029
|
-
message = lines.find((l) => l.trim())?.trim() || "No error message found";
|
|
1030
|
-
}
|
|
1031
|
-
const rawExcerpt = lines.slice(0, 10).join("\n");
|
|
1032
|
-
return {
|
|
1033
|
-
error_type: errorType,
|
|
1034
|
-
file,
|
|
1035
|
-
line,
|
|
1036
|
-
column,
|
|
1037
|
-
message,
|
|
1038
|
-
stack_summary: stackFrames.slice(0, 5),
|
|
1039
|
-
// Limit to 5 frames
|
|
1040
|
-
raw_excerpt: rawExcerpt
|
|
1041
|
-
};
|
|
1042
|
-
}
|
|
1043
|
-
function isLibraryFrame(filePath) {
|
|
1044
|
-
const libraryPatterns = [
|
|
1045
|
-
/node_modules/,
|
|
1046
|
-
/site-packages/,
|
|
1047
|
-
/dist-packages/,
|
|
1048
|
-
/\.pnpm/,
|
|
1049
|
-
/\.yarn/,
|
|
1050
|
-
/internal\//,
|
|
1051
|
-
/^node:/,
|
|
1052
|
-
/<anonymous>/,
|
|
1053
|
-
/webpack-internal/,
|
|
1054
|
-
/react-dom/,
|
|
1055
|
-
/react-native/,
|
|
1056
|
-
/expo/,
|
|
1057
|
-
/metro/,
|
|
1058
|
-
/hermes/
|
|
1059
1304
|
];
|
|
1060
|
-
return libraryPatterns.some((pattern) => pattern.test(filePath));
|
|
1061
1305
|
}
|
|
1062
|
-
function
|
|
1306
|
+
function summarizeErrorsLocal(logText, maxLines = 100) {
|
|
1063
1307
|
const errorTypePatterns = [
|
|
1064
1308
|
{ type: "TypeError", pattern: /TypeError:\s*(.+)/i },
|
|
1065
1309
|
{ type: "SyntaxError", pattern: /SyntaxError:\s*(.+)/i },
|
|
@@ -1206,6 +1450,12 @@ function summarizeErrors(logText, maxLines = 100) {
|
|
|
1206
1450
|
}
|
|
1207
1451
|
|
|
1208
1452
|
// src/resources/index.ts
|
|
1453
|
+
function isErrorTask(task) {
|
|
1454
|
+
if (task.priority !== "high") return false;
|
|
1455
|
+
const title = task.title?.toLowerCase() || "";
|
|
1456
|
+
const patterns = ["error", "bug", "fix", "crash", "fail", "broken", "issue", "typescript", "ts"];
|
|
1457
|
+
return patterns.some((p) => title.includes(p)) || task.sentry_issue_id;
|
|
1458
|
+
}
|
|
1209
1459
|
function setupResources(taskOps) {
|
|
1210
1460
|
return [
|
|
1211
1461
|
// Active tasks resource
|
|
@@ -1303,6 +1553,167 @@ function setupResources(taskOps) {
|
|
|
1303
1553
|
]
|
|
1304
1554
|
};
|
|
1305
1555
|
}
|
|
1556
|
+
},
|
|
1557
|
+
// Error Summary resource (lightweight - just counts)
|
|
1558
|
+
{
|
|
1559
|
+
uri: "vibetasks://errors/summary",
|
|
1560
|
+
name: "Error Summary",
|
|
1561
|
+
description: "Current error counts by priority. Check this before asking about specific errors.",
|
|
1562
|
+
mimeType: "application/json",
|
|
1563
|
+
annotations: {
|
|
1564
|
+
audience: ["assistant"],
|
|
1565
|
+
priority: 0.8
|
|
1566
|
+
// High priority for AI awareness
|
|
1567
|
+
},
|
|
1568
|
+
handler: async (taskOps2) => {
|
|
1569
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
1570
|
+
const errorTasks = allTasks.filter(isErrorTask);
|
|
1571
|
+
const unacknowledged = errorTasks.filter((t) => !t.context_notes?.includes("[AI_ACKNOWLEDGED]"));
|
|
1572
|
+
const critical = errorTasks.filter((t) => t.title?.toLowerCase().includes("critical") || t.sentry_level === "fatal");
|
|
1573
|
+
const fromSentry = errorTasks.filter((t) => t.sentry_issue_id);
|
|
1574
|
+
const fromCI = errorTasks.filter((t) => t.tags?.some((tag) => tag.name === "ci"));
|
|
1575
|
+
return {
|
|
1576
|
+
contents: [
|
|
1577
|
+
{
|
|
1578
|
+
uri: "vibetasks://errors/summary",
|
|
1579
|
+
mimeType: "application/json",
|
|
1580
|
+
text: JSON.stringify(
|
|
1581
|
+
{
|
|
1582
|
+
total_errors: errorTasks.length,
|
|
1583
|
+
unacknowledged: unacknowledged.length,
|
|
1584
|
+
critical: critical.length,
|
|
1585
|
+
by_source: {
|
|
1586
|
+
sentry: fromSentry.length,
|
|
1587
|
+
ci: fromCI.length,
|
|
1588
|
+
manual: errorTasks.length - fromSentry.length - fromCI.length
|
|
1589
|
+
},
|
|
1590
|
+
needs_attention: critical.length > 0 || unacknowledged.length > 3,
|
|
1591
|
+
hint: unacknowledged.length > 0 ? "Use get_errors() tool to see details and acknowledge_error() when addressed." : "No unacknowledged errors."
|
|
1592
|
+
},
|
|
1593
|
+
null,
|
|
1594
|
+
2
|
|
1595
|
+
)
|
|
1596
|
+
}
|
|
1597
|
+
]
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
},
|
|
1601
|
+
// Error List resource (more detail, still concise)
|
|
1602
|
+
{
|
|
1603
|
+
uri: "vibetasks://errors/list",
|
|
1604
|
+
name: "Error List",
|
|
1605
|
+
description: "List of current errors with basic details. Use get_error_details tool for full context.",
|
|
1606
|
+
mimeType: "application/json",
|
|
1607
|
+
annotations: {
|
|
1608
|
+
audience: ["assistant"],
|
|
1609
|
+
priority: 0.6
|
|
1610
|
+
},
|
|
1611
|
+
handler: async (taskOps2) => {
|
|
1612
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
1613
|
+
const errorTasks = allTasks.filter(isErrorTask);
|
|
1614
|
+
const sorted = errorTasks.sort((a, b) => {
|
|
1615
|
+
const aAck = a.context_notes?.includes("[AI_ACKNOWLEDGED]") ? 1 : 0;
|
|
1616
|
+
const bAck = b.context_notes?.includes("[AI_ACKNOWLEDGED]") ? 1 : 0;
|
|
1617
|
+
if (aAck !== bAck) return aAck - bAck;
|
|
1618
|
+
return new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime();
|
|
1619
|
+
});
|
|
1620
|
+
return {
|
|
1621
|
+
contents: [
|
|
1622
|
+
{
|
|
1623
|
+
uri: "vibetasks://errors/list",
|
|
1624
|
+
mimeType: "application/json",
|
|
1625
|
+
text: JSON.stringify(
|
|
1626
|
+
{
|
|
1627
|
+
errors: sorted.slice(0, 10).map((t) => ({
|
|
1628
|
+
id: t.id,
|
|
1629
|
+
title: t.title,
|
|
1630
|
+
project: t.project_tag,
|
|
1631
|
+
status: t.status,
|
|
1632
|
+
acknowledged: t.context_notes?.includes("[AI_ACKNOWLEDGED]") || false,
|
|
1633
|
+
source: t.sentry_issue_id ? "sentry" : t.tags?.some((tag) => tag.name === "ci") ? "ci" : "manual",
|
|
1634
|
+
created: t.created_at,
|
|
1635
|
+
sentry_count: t.sentry_error_count
|
|
1636
|
+
})),
|
|
1637
|
+
total: errorTasks.length,
|
|
1638
|
+
showing: Math.min(10, errorTasks.length)
|
|
1639
|
+
},
|
|
1640
|
+
null,
|
|
1641
|
+
2
|
|
1642
|
+
)
|
|
1643
|
+
}
|
|
1644
|
+
]
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
// Inbox Summary resource - unified view of all external sources
|
|
1649
|
+
{
|
|
1650
|
+
uri: "vibetasks://inbox/summary",
|
|
1651
|
+
name: "Inbox Summary",
|
|
1652
|
+
description: "Summary of inbox items from all sources (Sentry, GitHub, Email, Slack, CI). Check this to see what needs attention.",
|
|
1653
|
+
mimeType: "application/json",
|
|
1654
|
+
annotations: {
|
|
1655
|
+
audience: ["assistant"],
|
|
1656
|
+
priority: 0.9
|
|
1657
|
+
// High priority - AI should check inbox often
|
|
1658
|
+
},
|
|
1659
|
+
handler: async (taskOps2) => {
|
|
1660
|
+
const stats = await taskOps2.getInboxStats();
|
|
1661
|
+
return {
|
|
1662
|
+
contents: [
|
|
1663
|
+
{
|
|
1664
|
+
uri: "vibetasks://inbox/summary",
|
|
1665
|
+
mimeType: "application/json",
|
|
1666
|
+
text: JSON.stringify(
|
|
1667
|
+
{
|
|
1668
|
+
...stats,
|
|
1669
|
+
hint: stats.needs_attention ? "Use get_inbox() tool to see items that need attention." : "Inbox is under control!"
|
|
1670
|
+
},
|
|
1671
|
+
null,
|
|
1672
|
+
2
|
|
1673
|
+
)
|
|
1674
|
+
}
|
|
1675
|
+
]
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
},
|
|
1679
|
+
// Inbox List resource - detailed inbox items
|
|
1680
|
+
{
|
|
1681
|
+
uri: "vibetasks://inbox/list",
|
|
1682
|
+
name: "Inbox Items",
|
|
1683
|
+
description: "List of inbox items from all external sources. Shows up to 15 items sorted by priority and recency.",
|
|
1684
|
+
mimeType: "application/json",
|
|
1685
|
+
annotations: {
|
|
1686
|
+
audience: ["assistant"],
|
|
1687
|
+
priority: 0.7
|
|
1688
|
+
},
|
|
1689
|
+
handler: async (taskOps2) => {
|
|
1690
|
+
const inboxItems = await taskOps2.getInboxItems({ limit: 15 });
|
|
1691
|
+
return {
|
|
1692
|
+
contents: [
|
|
1693
|
+
{
|
|
1694
|
+
uri: "vibetasks://inbox/list",
|
|
1695
|
+
mimeType: "application/json",
|
|
1696
|
+
text: JSON.stringify(
|
|
1697
|
+
{
|
|
1698
|
+
inbox_items: inboxItems.map((t) => ({
|
|
1699
|
+
id: t.id,
|
|
1700
|
+
title: t.title,
|
|
1701
|
+
source: t.source_type || (t.sentry_issue_id ? "sentry" : t.github_issue_id ? "github" : t.tags?.some((tag) => tag.name === "ci") ? "ci" : "manual"),
|
|
1702
|
+
priority: t.priority,
|
|
1703
|
+
status: t.status,
|
|
1704
|
+
project: t.project_tag,
|
|
1705
|
+
created_at: t.created_at,
|
|
1706
|
+
acknowledged: t.context_notes?.includes("[AI_ACKNOWLEDGED]") || false
|
|
1707
|
+
})),
|
|
1708
|
+
total: inboxItems.length
|
|
1709
|
+
},
|
|
1710
|
+
null,
|
|
1711
|
+
2
|
|
1712
|
+
)
|
|
1713
|
+
}
|
|
1714
|
+
]
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1306
1717
|
}
|
|
1307
1718
|
];
|
|
1308
1719
|
}
|
|
@@ -1321,7 +1732,7 @@ if (hookType === "SessionStart") {
|
|
|
1321
1732
|
async function main() {
|
|
1322
1733
|
try {
|
|
1323
1734
|
const authManager = new AuthManager();
|
|
1324
|
-
const supabaseUrl = process.env.TASKFLOW_SUPABASE_URL || await authManager.getConfig("supabase_url") || "https://
|
|
1735
|
+
const supabaseUrl = process.env.TASKFLOW_SUPABASE_URL || await authManager.getConfig("supabase_url") || "https://ihmayqzxqyednchbezya.supabase.co";
|
|
1325
1736
|
const supabaseKey = process.env.TASKFLOW_SUPABASE_KEY || await authManager.getConfig("supabase_key") || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNia2t6dGJjb2l0cmZjbGVnaGZkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc3NTc0MjgsImV4cCI6MjA4MzMzMzQyOH0.G7ILx-nntP0NbxO1gKt5yASb7nt7OmpJ8qtykeGYbQA";
|
|
1326
1737
|
const accessToken = await authManager.getAccessToken();
|
|
1327
1738
|
if (!accessToken) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibetasks/mcp-server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "VibeTasks MCP Server for Claude Code, Cursor, and AI coding tools. Status-based task management: todo → vibing → done.",
|
|
5
5
|
"author": "Vyas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
48
|
-
"@vibetasks/core": "^0.5.
|
|
48
|
+
"@vibetasks/core": "^0.5.4",
|
|
49
|
+
"@vibetasks/shared": "*",
|
|
49
50
|
"zod": "^3.22.0"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|