projoflow-mcp-server 1.1.4 → 1.2.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/index.js +449 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -79,7 +79,26 @@ async function authenticate() {
|
|
|
79
79
|
}
|
|
80
80
|
console.error(`Raw projectIds response: ${JSON.stringify(projectIds)}`);
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
// Handle different return formats from Supabase RPC:
|
|
83
|
+
// - Direct array: ["uuid1", "uuid2"]
|
|
84
|
+
// - Wrapped: [{"get_client_project_ids": ["uuid1"]}]
|
|
85
|
+
// - Single value: "uuid1"
|
|
86
|
+
// - Null: null
|
|
87
|
+
let parsedProjectIds = [];
|
|
88
|
+
if (Array.isArray(projectIds)) {
|
|
89
|
+
if (projectIds.length > 0 && typeof projectIds[0] === 'object' && projectIds[0].get_client_project_ids) {
|
|
90
|
+
// Wrapped format from SQL query
|
|
91
|
+
parsedProjectIds = projectIds[0].get_client_project_ids || [];
|
|
92
|
+
} else {
|
|
93
|
+
// Direct array format
|
|
94
|
+
parsedProjectIds = projectIds;
|
|
95
|
+
}
|
|
96
|
+
} else if (typeof projectIds === 'string') {
|
|
97
|
+
parsedProjectIds = [projectIds];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
authContext.accessible_project_ids = parsedProjectIds;
|
|
101
|
+
console.error(`Parsed project IDs: ${JSON.stringify(parsedProjectIds)}`);
|
|
83
102
|
console.error(`Authenticated as CLIENT via API key (${authContext.accessible_project_ids.length} projects accessible)`);
|
|
84
103
|
} else {
|
|
85
104
|
console.error(`Authenticated via API key for workspace ${authContext.workspace_id}`);
|
|
@@ -111,7 +130,20 @@ async function authenticate() {
|
|
|
111
130
|
p_user_id: authContext.user_id,
|
|
112
131
|
p_client_id: authContext.client_id
|
|
113
132
|
});
|
|
114
|
-
|
|
133
|
+
|
|
134
|
+
// Handle different return formats (same as above)
|
|
135
|
+
let parsedProjectIds = [];
|
|
136
|
+
if (Array.isArray(projectIds)) {
|
|
137
|
+
if (projectIds.length > 0 && typeof projectIds[0] === 'object' && projectIds[0].get_client_project_ids) {
|
|
138
|
+
parsedProjectIds = projectIds[0].get_client_project_ids || [];
|
|
139
|
+
} else {
|
|
140
|
+
parsedProjectIds = projectIds;
|
|
141
|
+
}
|
|
142
|
+
} else if (typeof projectIds === 'string') {
|
|
143
|
+
parsedProjectIds = [projectIds];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
authContext.accessible_project_ids = parsedProjectIds;
|
|
115
147
|
console.error(`Authenticated as CLIENT via API key (${authContext.accessible_project_ids.length} projects accessible)`);
|
|
116
148
|
} else {
|
|
117
149
|
// Update last_used_at
|
|
@@ -243,7 +275,7 @@ const TOOLS = [
|
|
|
243
275
|
},
|
|
244
276
|
{
|
|
245
277
|
name: "update_task",
|
|
246
|
-
description: "Update a task's details or
|
|
278
|
+
description: "Update a task's details, status, or assignment. Use list_workspace_members to find user IDs.",
|
|
247
279
|
inputSchema: {
|
|
248
280
|
type: "object",
|
|
249
281
|
properties: {
|
|
@@ -252,7 +284,9 @@ const TOOLS = [
|
|
|
252
284
|
description: { type: "string" },
|
|
253
285
|
status: { type: "string", enum: ["todo", "in_progress", "review", "done"] },
|
|
254
286
|
priority: { type: "string", enum: ["low", "medium", "high", "urgent"] },
|
|
255
|
-
due_date: { type: "string" }
|
|
287
|
+
due_date: { type: "string", description: "Due date (YYYY-MM-DD)" },
|
|
288
|
+
assigned_to: { type: "string", description: "User ID to assign/reassign the task to (get from list_workspace_members)" },
|
|
289
|
+
estimated_hours: { type: "number", description: "Estimated hours to complete" }
|
|
256
290
|
},
|
|
257
291
|
required: ["task_id"]
|
|
258
292
|
}
|
|
@@ -457,6 +491,218 @@ const TOOLS = [
|
|
|
457
491
|
},
|
|
458
492
|
required: ["file_path"]
|
|
459
493
|
}
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: "get_task",
|
|
497
|
+
description: "Get full details of a specific task including assignee info",
|
|
498
|
+
inputSchema: {
|
|
499
|
+
type: "object",
|
|
500
|
+
properties: {
|
|
501
|
+
task_id: { type: "string", description: "Task ID" }
|
|
502
|
+
},
|
|
503
|
+
required: ["task_id"]
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: "delete_task",
|
|
508
|
+
description: "Delete a task permanently",
|
|
509
|
+
inputSchema: {
|
|
510
|
+
type: "object",
|
|
511
|
+
properties: {
|
|
512
|
+
task_id: { type: "string", description: "Task ID" }
|
|
513
|
+
},
|
|
514
|
+
required: ["task_id"]
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: "get_client",
|
|
519
|
+
description: "Get full details of a specific client",
|
|
520
|
+
inputSchema: {
|
|
521
|
+
type: "object",
|
|
522
|
+
properties: {
|
|
523
|
+
client_id: { type: "string", description: "Client ID" }
|
|
524
|
+
},
|
|
525
|
+
required: ["client_id"]
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: "update_client",
|
|
530
|
+
description: "Update a client's details",
|
|
531
|
+
inputSchema: {
|
|
532
|
+
type: "object",
|
|
533
|
+
properties: {
|
|
534
|
+
client_id: { type: "string", description: "Client ID" },
|
|
535
|
+
name: { type: "string" },
|
|
536
|
+
contact_name: { type: "string" },
|
|
537
|
+
email: { type: "string" },
|
|
538
|
+
phone: { type: "string" },
|
|
539
|
+
company: { type: "string" },
|
|
540
|
+
notes: { type: "string" }
|
|
541
|
+
},
|
|
542
|
+
required: ["client_id"]
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: "delete_client",
|
|
547
|
+
description: "Delete a client permanently. Will fail if client has linked projects.",
|
|
548
|
+
inputSchema: {
|
|
549
|
+
type: "object",
|
|
550
|
+
properties: {
|
|
551
|
+
client_id: { type: "string", description: "Client ID" }
|
|
552
|
+
},
|
|
553
|
+
required: ["client_id"]
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "create_lead",
|
|
558
|
+
description: "Create a new lead/inquiry",
|
|
559
|
+
inputSchema: {
|
|
560
|
+
type: "object",
|
|
561
|
+
properties: {
|
|
562
|
+
company_name: { type: "string", description: "Company or person name" },
|
|
563
|
+
contact_name: { type: "string", description: "Contact person name" },
|
|
564
|
+
email: { type: "string" },
|
|
565
|
+
phone: { type: "string" },
|
|
566
|
+
budget_range: { type: "string", enum: ["under_5k", "5k_10k", "10k_25k", "25k_50k", "50k_plus", "not_sure"] },
|
|
567
|
+
timeline: { type: "string", enum: ["asap", "1_month", "2_3_months", "3_6_months", "flexible"] },
|
|
568
|
+
project_description: { type: "string", description: "What the lead needs" },
|
|
569
|
+
source: { type: "string", description: "How they found you" },
|
|
570
|
+
notes: { type: "string" },
|
|
571
|
+
status: { type: "string", enum: ["new", "contacted", "qualified", "converted", "lost"], default: "new" }
|
|
572
|
+
},
|
|
573
|
+
required: ["company_name", "email"]
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
name: "get_lead",
|
|
578
|
+
description: "Get full details of a specific lead",
|
|
579
|
+
inputSchema: {
|
|
580
|
+
type: "object",
|
|
581
|
+
properties: {
|
|
582
|
+
lead_id: { type: "string", description: "Lead ID" }
|
|
583
|
+
},
|
|
584
|
+
required: ["lead_id"]
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
name: "delete_lead",
|
|
589
|
+
description: "Delete a lead permanently",
|
|
590
|
+
inputSchema: {
|
|
591
|
+
type: "object",
|
|
592
|
+
properties: {
|
|
593
|
+
lead_id: { type: "string", description: "Lead ID" }
|
|
594
|
+
},
|
|
595
|
+
required: ["lead_id"]
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "list_notes",
|
|
600
|
+
description: "List notes for a project, optionally filtered by type",
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: "object",
|
|
603
|
+
properties: {
|
|
604
|
+
project_id: { type: "string", description: "Project ID" },
|
|
605
|
+
note_type: { type: "string", enum: ["general", "meeting", "technical", "decision"], description: "Filter by note type" }
|
|
606
|
+
},
|
|
607
|
+
required: ["project_id"]
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
name: "update_note",
|
|
612
|
+
description: "Update a project note",
|
|
613
|
+
inputSchema: {
|
|
614
|
+
type: "object",
|
|
615
|
+
properties: {
|
|
616
|
+
note_id: { type: "string", description: "Note ID" },
|
|
617
|
+
title: { type: "string" },
|
|
618
|
+
content: { type: "string" },
|
|
619
|
+
note_type: { type: "string", enum: ["general", "meeting", "technical", "decision"] }
|
|
620
|
+
},
|
|
621
|
+
required: ["note_id"]
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: "delete_note",
|
|
626
|
+
description: "Delete a project note permanently",
|
|
627
|
+
inputSchema: {
|
|
628
|
+
type: "object",
|
|
629
|
+
properties: {
|
|
630
|
+
note_id: { type: "string", description: "Note ID" }
|
|
631
|
+
},
|
|
632
|
+
required: ["note_id"]
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
name: "delete_document",
|
|
637
|
+
description: "Delete a project document from storage and database",
|
|
638
|
+
inputSchema: {
|
|
639
|
+
type: "object",
|
|
640
|
+
properties: {
|
|
641
|
+
document_id: { type: "string", description: "Document ID from list_project_documents" }
|
|
642
|
+
},
|
|
643
|
+
required: ["document_id"]
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
name: "update_time_entry",
|
|
648
|
+
description: "Update a time entry",
|
|
649
|
+
inputSchema: {
|
|
650
|
+
type: "object",
|
|
651
|
+
properties: {
|
|
652
|
+
time_entry_id: { type: "string", description: "Time entry ID" },
|
|
653
|
+
duration_minutes: { type: "number" },
|
|
654
|
+
description: { type: "string" },
|
|
655
|
+
date: { type: "string", description: "Date (YYYY-MM-DD)" },
|
|
656
|
+
billable: { type: "boolean" },
|
|
657
|
+
task_id: { type: "string" }
|
|
658
|
+
},
|
|
659
|
+
required: ["time_entry_id"]
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
name: "delete_time_entry",
|
|
664
|
+
description: "Delete a time entry permanently",
|
|
665
|
+
inputSchema: {
|
|
666
|
+
type: "object",
|
|
667
|
+
properties: {
|
|
668
|
+
time_entry_id: { type: "string", description: "Time entry ID" }
|
|
669
|
+
},
|
|
670
|
+
required: ["time_entry_id"]
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: "update_comment",
|
|
675
|
+
description: "Update a task comment's content",
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: "object",
|
|
678
|
+
properties: {
|
|
679
|
+
comment_id: { type: "string", description: "Comment ID" },
|
|
680
|
+
content: { type: "string", description: "New comment content" }
|
|
681
|
+
},
|
|
682
|
+
required: ["comment_id", "content"]
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
name: "delete_comment",
|
|
687
|
+
description: "Delete a task comment permanently",
|
|
688
|
+
inputSchema: {
|
|
689
|
+
type: "object",
|
|
690
|
+
properties: {
|
|
691
|
+
comment_id: { type: "string", description: "Comment ID" }
|
|
692
|
+
},
|
|
693
|
+
required: ["comment_id"]
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
name: "delete_project",
|
|
698
|
+
description: "Delete a project and all its related data (tasks, time entries, notes, documents) permanently",
|
|
699
|
+
inputSchema: {
|
|
700
|
+
type: "object",
|
|
701
|
+
properties: {
|
|
702
|
+
project_id: { type: "string", description: "Project ID" }
|
|
703
|
+
},
|
|
704
|
+
required: ["project_id"]
|
|
705
|
+
}
|
|
460
706
|
}
|
|
461
707
|
];
|
|
462
708
|
|
|
@@ -908,6 +1154,205 @@ async function handleTool(name, args) {
|
|
|
908
1154
|
};
|
|
909
1155
|
}
|
|
910
1156
|
|
|
1157
|
+
case "get_task": {
|
|
1158
|
+
const { data, error } = await supabase
|
|
1159
|
+
.from("tasks")
|
|
1160
|
+
.select("*, users(id, email, name), projects(id, name)")
|
|
1161
|
+
.eq("id", args.task_id)
|
|
1162
|
+
.single();
|
|
1163
|
+
if (error) throw new Error(error.message);
|
|
1164
|
+
return data;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
case "delete_task": {
|
|
1168
|
+
const { error } = await supabase
|
|
1169
|
+
.from("tasks")
|
|
1170
|
+
.delete()
|
|
1171
|
+
.eq("id", args.task_id);
|
|
1172
|
+
if (error) throw new Error(error.message);
|
|
1173
|
+
return { success: true, message: "Task deleted" };
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
case "get_client": {
|
|
1177
|
+
const { data, error } = await supabase
|
|
1178
|
+
.from("clients")
|
|
1179
|
+
.select("*")
|
|
1180
|
+
.eq("id", args.client_id)
|
|
1181
|
+
.single();
|
|
1182
|
+
if (error) throw new Error(error.message);
|
|
1183
|
+
return data;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
case "update_client": {
|
|
1187
|
+
const { client_id, ...updates } = args;
|
|
1188
|
+
const { data, error } = await supabase
|
|
1189
|
+
.from("clients")
|
|
1190
|
+
.update(updates)
|
|
1191
|
+
.eq("id", client_id)
|
|
1192
|
+
.select()
|
|
1193
|
+
.single();
|
|
1194
|
+
if (error) throw new Error(error.message);
|
|
1195
|
+
return data;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
case "delete_client": {
|
|
1199
|
+
const { error } = await supabase
|
|
1200
|
+
.from("clients")
|
|
1201
|
+
.delete()
|
|
1202
|
+
.eq("id", args.client_id);
|
|
1203
|
+
if (error) throw new Error(error.message);
|
|
1204
|
+
return { success: true, message: "Client deleted" };
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
case "create_lead": {
|
|
1208
|
+
const { data, error } = await supabase
|
|
1209
|
+
.from("leads")
|
|
1210
|
+
.insert({
|
|
1211
|
+
company_name: args.company_name,
|
|
1212
|
+
contact_name: args.contact_name,
|
|
1213
|
+
email: args.email,
|
|
1214
|
+
phone: args.phone,
|
|
1215
|
+
budget_range: args.budget_range,
|
|
1216
|
+
timeline: args.timeline,
|
|
1217
|
+
project_description: args.project_description,
|
|
1218
|
+
source: args.source,
|
|
1219
|
+
notes: args.notes,
|
|
1220
|
+
status: args.status || "new"
|
|
1221
|
+
})
|
|
1222
|
+
.select()
|
|
1223
|
+
.single();
|
|
1224
|
+
if (error) throw new Error(error.message);
|
|
1225
|
+
return data;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
case "get_lead": {
|
|
1229
|
+
const { data, error } = await supabase
|
|
1230
|
+
.from("leads")
|
|
1231
|
+
.select("*")
|
|
1232
|
+
.eq("id", args.lead_id)
|
|
1233
|
+
.single();
|
|
1234
|
+
if (error) throw new Error(error.message);
|
|
1235
|
+
return data;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
case "delete_lead": {
|
|
1239
|
+
const { error } = await supabase
|
|
1240
|
+
.from("leads")
|
|
1241
|
+
.delete()
|
|
1242
|
+
.eq("id", args.lead_id);
|
|
1243
|
+
if (error) throw new Error(error.message);
|
|
1244
|
+
return { success: true, message: "Lead deleted" };
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
case "list_notes": {
|
|
1248
|
+
let query = supabase
|
|
1249
|
+
.from("notes")
|
|
1250
|
+
.select("*")
|
|
1251
|
+
.eq("project_id", args.project_id)
|
|
1252
|
+
.order("created_at", { ascending: false });
|
|
1253
|
+
if (args.note_type) query = query.eq("note_type", args.note_type);
|
|
1254
|
+
const { data, error } = await query;
|
|
1255
|
+
if (error) throw new Error(error.message);
|
|
1256
|
+
return data;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
case "update_note": {
|
|
1260
|
+
const { note_id, ...updates } = args;
|
|
1261
|
+
const { data, error } = await supabase
|
|
1262
|
+
.from("notes")
|
|
1263
|
+
.update(updates)
|
|
1264
|
+
.eq("id", note_id)
|
|
1265
|
+
.select()
|
|
1266
|
+
.single();
|
|
1267
|
+
if (error) throw new Error(error.message);
|
|
1268
|
+
return data;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
case "delete_note": {
|
|
1272
|
+
const { error } = await supabase
|
|
1273
|
+
.from("notes")
|
|
1274
|
+
.delete()
|
|
1275
|
+
.eq("id", args.note_id);
|
|
1276
|
+
if (error) throw new Error(error.message);
|
|
1277
|
+
return { success: true, message: "Note deleted" };
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
case "delete_document": {
|
|
1281
|
+
// Get the file path first
|
|
1282
|
+
const { data: doc, error: fetchError } = await supabase
|
|
1283
|
+
.from("project_documents")
|
|
1284
|
+
.select("file_path")
|
|
1285
|
+
.eq("id", args.document_id)
|
|
1286
|
+
.single();
|
|
1287
|
+
if (fetchError) throw new Error(fetchError.message);
|
|
1288
|
+
|
|
1289
|
+
// Delete from storage
|
|
1290
|
+
if (doc.file_path) {
|
|
1291
|
+
const { error: storageError } = await supabase.storage
|
|
1292
|
+
.from("project-documents")
|
|
1293
|
+
.remove([doc.file_path]);
|
|
1294
|
+
if (storageError) throw new Error(`Storage deletion failed: ${storageError.message}`);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// Delete from database
|
|
1298
|
+
const { error } = await supabase
|
|
1299
|
+
.from("project_documents")
|
|
1300
|
+
.delete()
|
|
1301
|
+
.eq("id", args.document_id);
|
|
1302
|
+
if (error) throw new Error(error.message);
|
|
1303
|
+
return { success: true, message: "Document deleted" };
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
case "update_time_entry": {
|
|
1307
|
+
const { time_entry_id, ...updates } = args;
|
|
1308
|
+
const { data, error } = await supabase
|
|
1309
|
+
.from("time_entries")
|
|
1310
|
+
.update(updates)
|
|
1311
|
+
.eq("id", time_entry_id)
|
|
1312
|
+
.select("*, projects(name), tasks(title)")
|
|
1313
|
+
.single();
|
|
1314
|
+
if (error) throw new Error(error.message);
|
|
1315
|
+
return data;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
case "delete_time_entry": {
|
|
1319
|
+
const { error } = await supabase
|
|
1320
|
+
.from("time_entries")
|
|
1321
|
+
.delete()
|
|
1322
|
+
.eq("id", args.time_entry_id);
|
|
1323
|
+
if (error) throw new Error(error.message);
|
|
1324
|
+
return { success: true, message: "Time entry deleted" };
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
case "update_comment": {
|
|
1328
|
+
const { data, error } = await supabase
|
|
1329
|
+
.from("task_comments")
|
|
1330
|
+
.update({ content: args.content })
|
|
1331
|
+
.eq("id", args.comment_id)
|
|
1332
|
+
.select()
|
|
1333
|
+
.single();
|
|
1334
|
+
if (error) throw new Error(error.message);
|
|
1335
|
+
return data;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
case "delete_comment": {
|
|
1339
|
+
const { error } = await supabase
|
|
1340
|
+
.from("task_comments")
|
|
1341
|
+
.delete()
|
|
1342
|
+
.eq("id", args.comment_id);
|
|
1343
|
+
if (error) throw new Error(error.message);
|
|
1344
|
+
return { success: true, message: "Comment deleted" };
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
case "delete_project": {
|
|
1348
|
+
const { error } = await supabase
|
|
1349
|
+
.from("projects")
|
|
1350
|
+
.delete()
|
|
1351
|
+
.eq("id", args.project_id);
|
|
1352
|
+
if (error) throw new Error(error.message);
|
|
1353
|
+
return { success: true, message: "Project and all related data deleted" };
|
|
1354
|
+
}
|
|
1355
|
+
|
|
911
1356
|
default:
|
|
912
1357
|
throw new Error(`Unknown tool: ${name}`);
|
|
913
1358
|
}
|