@studiometa/productive-mcp 0.9.2 → 0.10.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/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/formatters.d.ts +0 -4
- package/dist/formatters.d.ts.map +1 -1
- package/dist/handlers/attachments.d.ts +5 -2
- package/dist/handlers/attachments.d.ts.map +1 -1
- package/dist/handlers/bookings.d.ts +5 -2
- package/dist/handlers/bookings.d.ts.map +1 -1
- package/dist/handlers/comments.d.ts +5 -2
- package/dist/handlers/comments.d.ts.map +1 -1
- package/dist/handlers/companies.d.ts +11 -5
- package/dist/handlers/companies.d.ts.map +1 -1
- package/dist/handlers/deals.d.ts +4 -5
- package/dist/handlers/deals.d.ts.map +1 -1
- package/dist/handlers/discussions.d.ts +5 -9
- package/dist/handlers/discussions.d.ts.map +1 -1
- package/dist/handlers/factory.d.ts +116 -0
- package/dist/handlers/factory.d.ts.map +1 -0
- package/dist/handlers/help.d.ts.map +1 -1
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/handlers/pages.d.ts +12 -9
- package/dist/handlers/pages.d.ts.map +1 -1
- package/dist/handlers/projects.d.ts +11 -5
- package/dist/handlers/projects.d.ts.map +1 -1
- package/dist/handlers/services.d.ts +12 -2
- package/dist/handlers/services.d.ts.map +1 -1
- package/dist/handlers/tasks.d.ts +4 -5
- package/dist/handlers/tasks.d.ts.map +1 -1
- package/dist/handlers/time.d.ts +4 -6
- package/dist/handlers/time.d.ts.map +1 -1
- package/dist/handlers/timers.d.ts +5 -2
- package/dist/handlers/timers.d.ts.map +1 -1
- package/dist/handlers/types.d.ts +6 -0
- package/dist/handlers/types.d.ts.map +1 -1
- package/dist/{handlers-D4tRd30c.js → handlers-BE3CYyVX.js} +636 -897
- package/dist/handlers-BE3CYyVX.js.map +1 -0
- package/dist/handlers.js +1 -1
- package/dist/hints.d.ts +0 -4
- package/dist/hints.d.ts.map +1 -1
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/schema.d.ts +0 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/stdio.js +1 -1
- package/dist/{version-IB2ulmSy.js → version-BKUpCCHx.js} +2 -2
- package/dist/{version-IB2ulmSy.js.map → version-BKUpCCHx.js.map} +1 -1
- package/package.json +3 -3
- package/skills/SKILL.md +31 -54
- package/dist/handlers/budgets.d.ts +0 -9
- package/dist/handlers/budgets.d.ts.map +0 -1
- package/dist/handlers-D4tRd30c.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ProductiveApi, formatAttachment, formatBooking,
|
|
2
|
-
import { RESOURCES, ResolveError, VALID_REPORT_TYPES, createBooking, createComment, createCompany, createDeal, createDiscussion, createPage, createTask, createTimeEntry, deleteAttachment, deleteDiscussion, deletePage, fromHandlerContext, getAttachment, getBooking,
|
|
1
|
+
import { ProductiveApi, formatAttachment, formatBooking, formatComment, formatCompany, formatDeal, formatDiscussion, formatListResponse, formatPage, formatPerson, formatProject, formatService, formatTask, formatTimeEntry, formatTimer } from "@studiometa/productive-api";
|
|
2
|
+
import { RESOURCES, ResolveError, VALID_REPORT_TYPES, createBooking, createComment, createCompany, createDeal, createDiscussion, createPage, createTask, createTimeEntry, deleteAttachment, deleteDiscussion, deletePage, deleteTimeEntry, fromHandlerContext, getAttachment, getBooking, getComment, getCompany, getDeal, getDiscussion, getPage, getPerson, getProject, getReport, getTask, getTimeEntry, getTimer, listAttachments, listBookings, listComments, listCompanies, listDeals, listDiscussions, listPages, listPeople, listProjects, listServices, listTasks, listTimeEntries, listTimers, reopenDiscussion, resolveDiscussion, resolveResource, startTimer, stopTimer, updateBooking, updateComment, updateCompany, updateDeal, updateDiscussion, updatePage, updateTask, updateTimeEntry } from "@studiometa/productive-core";
|
|
3
3
|
/**
|
|
4
4
|
* Custom error classes for MCP server
|
|
5
5
|
*
|
|
@@ -42,6 +42,7 @@ const ErrorMessages = {
|
|
|
42
42
|
noUserIdConfigured: () => new UserInputError("User ID not configured", ["The \"me\" action requires a user ID to be configured", "Use action=\"list\" to find people, or configure the user ID"]),
|
|
43
43
|
missingCommentTarget: () => new UserInputError("A target is required for creating a comment", ["Provide one of: task_id, deal_id, or company_id", "Find targets using resource=\"tasks\", \"deals\", or \"companies\" with action=\"list\""]),
|
|
44
44
|
missingBookingTarget: () => new UserInputError("A service or event is required for creating a booking", ["Provide either: service_id or event_id", "Find services using resource=\"services\" with action=\"list\""]),
|
|
45
|
+
noUpdateFieldsSpecified: (allowedFields) => new UserInputError(`No updates specified. Provide at least one of: ${allowedFields.join(", ")}`, ["Specify at least one field to update", `Updatable fields are: ${allowedFields.join(", ")}`]),
|
|
45
46
|
apiError: (statusCode, message) => {
|
|
46
47
|
const hints = [];
|
|
47
48
|
if (statusCode === 401) {
|
|
@@ -156,14 +157,6 @@ function formatService$1(service, options) {
|
|
|
156
157
|
return result;
|
|
157
158
|
}
|
|
158
159
|
/**
|
|
159
|
-
* Format budget for agent consumption
|
|
160
|
-
*/
|
|
161
|
-
function formatBudget$1(budget, options) {
|
|
162
|
-
const result = formatBudget(budget, MCP_FORMAT_OPTIONS);
|
|
163
|
-
if (options?.compact) return compactify(result, ["budget_type", "currency"]);
|
|
164
|
-
return result;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
160
|
* Format company for agent consumption
|
|
168
161
|
*/
|
|
169
162
|
function formatCompany$1(company, options) {
|
|
@@ -434,40 +427,6 @@ function getDealHints(dealId) {
|
|
|
434
427
|
};
|
|
435
428
|
}
|
|
436
429
|
/**
|
|
437
|
-
* Generate hints for a budget
|
|
438
|
-
*/
|
|
439
|
-
function getBudgetHints(budgetId) {
|
|
440
|
-
return { related_resources: [
|
|
441
|
-
{
|
|
442
|
-
resource: "services",
|
|
443
|
-
description: "Get services (budget lines) for this budget",
|
|
444
|
-
example: {
|
|
445
|
-
resource: "services",
|
|
446
|
-
action: "list",
|
|
447
|
-
filter: { budget_id: budgetId }
|
|
448
|
-
}
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
resource: "time",
|
|
452
|
-
description: "Get time entries for this budget",
|
|
453
|
-
example: {
|
|
454
|
-
resource: "time",
|
|
455
|
-
action: "list",
|
|
456
|
-
filter: { budget_id: budgetId }
|
|
457
|
-
}
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
resource: "bookings",
|
|
461
|
-
description: "Get bookings for this budget",
|
|
462
|
-
example: {
|
|
463
|
-
resource: "bookings",
|
|
464
|
-
action: "list",
|
|
465
|
-
filter: { budget_id: budgetId }
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
] };
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
430
|
* Generate hints for a person
|
|
472
431
|
*/
|
|
473
432
|
function getPersonHints(personId) {
|
|
@@ -848,236 +807,6 @@ function toStringFilter(filter) {
|
|
|
848
807
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
849
808
|
}
|
|
850
809
|
/**
|
|
851
|
-
* Attachments MCP handler.
|
|
852
|
-
*/
|
|
853
|
-
var VALID_ACTIONS$14 = [
|
|
854
|
-
"list",
|
|
855
|
-
"get",
|
|
856
|
-
"delete"
|
|
857
|
-
];
|
|
858
|
-
async function handleAttachments(action, args, ctx) {
|
|
859
|
-
const { formatOptions, filter, page, perPage } = ctx;
|
|
860
|
-
const { id, task_id, comment_id, deal_id } = args;
|
|
861
|
-
const execCtx = ctx.executor();
|
|
862
|
-
if (action === "get") {
|
|
863
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
|
|
864
|
-
const result = await getAttachment({ id }, execCtx);
|
|
865
|
-
const formatted = formatAttachment$1(result.data, formatOptions);
|
|
866
|
-
if (ctx.includeHints !== false) {
|
|
867
|
-
const attachableType = result.data.attributes?.attachable_type;
|
|
868
|
-
return jsonResult({
|
|
869
|
-
...formatted,
|
|
870
|
-
_hints: getAttachmentHints(id, attachableType)
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
return jsonResult(formatted);
|
|
874
|
-
}
|
|
875
|
-
if (action === "delete") {
|
|
876
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("delete"));
|
|
877
|
-
await deleteAttachment({ id }, execCtx);
|
|
878
|
-
return jsonResult({
|
|
879
|
-
success: true,
|
|
880
|
-
deleted: id
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
if (action === "list") {
|
|
884
|
-
const options = { ...filter };
|
|
885
|
-
if (task_id) options.task_id = task_id;
|
|
886
|
-
if (comment_id) options.comment_id = comment_id;
|
|
887
|
-
if (deal_id) options.deal_id = deal_id;
|
|
888
|
-
const result = await listAttachments({
|
|
889
|
-
page,
|
|
890
|
-
perPage,
|
|
891
|
-
additionalFilters: options
|
|
892
|
-
}, execCtx);
|
|
893
|
-
return jsonResult(formatListResponse$1(result.data, formatAttachment$1, result.meta, formatOptions));
|
|
894
|
-
}
|
|
895
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "attachments", VALID_ACTIONS$14));
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* Bookings MCP handler.
|
|
899
|
-
*/
|
|
900
|
-
var VALID_ACTIONS$13 = [
|
|
901
|
-
"list",
|
|
902
|
-
"get",
|
|
903
|
-
"create",
|
|
904
|
-
"update"
|
|
905
|
-
];
|
|
906
|
-
async function handleBookings(action, args, ctx) {
|
|
907
|
-
const { formatOptions, filter, page, perPage, include: userInclude } = ctx;
|
|
908
|
-
const { id, person_id, service_id, event_id, started_on, ended_on, time, note } = args;
|
|
909
|
-
const include = userInclude?.length ? [...new Set([
|
|
910
|
-
"person",
|
|
911
|
-
"service",
|
|
912
|
-
...userInclude
|
|
913
|
-
])] : ["person", "service"];
|
|
914
|
-
const execCtx = ctx.executor();
|
|
915
|
-
if (action === "get") {
|
|
916
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
|
|
917
|
-
const result = await getBooking({
|
|
918
|
-
id,
|
|
919
|
-
include
|
|
920
|
-
}, execCtx);
|
|
921
|
-
const formatted = formatBooking$1(result.data, {
|
|
922
|
-
...formatOptions,
|
|
923
|
-
included: result.included
|
|
924
|
-
});
|
|
925
|
-
if (ctx.includeHints !== false) {
|
|
926
|
-
const personId = result.data.relationships?.person?.data?.id;
|
|
927
|
-
return jsonResult({
|
|
928
|
-
...formatted,
|
|
929
|
-
_hints: getBookingHints(id, personId)
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
return jsonResult(formatted);
|
|
933
|
-
}
|
|
934
|
-
if (action === "create") {
|
|
935
|
-
if (!person_id || !started_on || !ended_on) return inputErrorResult(ErrorMessages.missingRequiredFields("booking", [
|
|
936
|
-
"person_id",
|
|
937
|
-
"started_on",
|
|
938
|
-
"ended_on"
|
|
939
|
-
]));
|
|
940
|
-
if (!service_id && !event_id) return inputErrorResult(ErrorMessages.missingBookingTarget());
|
|
941
|
-
return jsonResult({
|
|
942
|
-
success: true,
|
|
943
|
-
...formatBooking$1((await createBooking({
|
|
944
|
-
personId: person_id,
|
|
945
|
-
serviceId: service_id ?? "",
|
|
946
|
-
startedOn: started_on,
|
|
947
|
-
endedOn: ended_on,
|
|
948
|
-
time,
|
|
949
|
-
note,
|
|
950
|
-
eventId: event_id
|
|
951
|
-
}, execCtx)).data, formatOptions)
|
|
952
|
-
});
|
|
953
|
-
}
|
|
954
|
-
if (action === "update") {
|
|
955
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
|
|
956
|
-
return jsonResult({
|
|
957
|
-
success: true,
|
|
958
|
-
...formatBooking$1((await updateBooking({
|
|
959
|
-
id,
|
|
960
|
-
startedOn: started_on,
|
|
961
|
-
endedOn: ended_on,
|
|
962
|
-
time,
|
|
963
|
-
note
|
|
964
|
-
}, execCtx)).data, formatOptions)
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
if (action === "list") {
|
|
968
|
-
const result = await listBookings({
|
|
969
|
-
page,
|
|
970
|
-
perPage,
|
|
971
|
-
additionalFilters: filter,
|
|
972
|
-
include
|
|
973
|
-
}, execCtx);
|
|
974
|
-
return jsonResult(formatListResponse$1(result.data, formatBooking$1, result.meta, formatOptions));
|
|
975
|
-
}
|
|
976
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "bookings", VALID_ACTIONS$13));
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Budgets MCP handler.
|
|
980
|
-
*
|
|
981
|
-
* Thin adapter that delegates business logic to core executors
|
|
982
|
-
* and handles MCP-specific concerns (hints, error formatting, JSON results).
|
|
983
|
-
*/
|
|
984
|
-
var VALID_ACTIONS$12 = ["list", "get"];
|
|
985
|
-
async function handleBudgets(action, args, ctx) {
|
|
986
|
-
const { formatOptions, filter, page, perPage } = ctx;
|
|
987
|
-
const { id } = args;
|
|
988
|
-
const execCtx = ctx.executor();
|
|
989
|
-
if (action === "get") {
|
|
990
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
|
|
991
|
-
const formatted = formatBudget$1((await getBudget({ id }, execCtx)).data, formatOptions);
|
|
992
|
-
if (ctx.includeHints !== false) return jsonResult({
|
|
993
|
-
...formatted,
|
|
994
|
-
_hints: getBudgetHints(id)
|
|
995
|
-
});
|
|
996
|
-
return jsonResult(formatted);
|
|
997
|
-
}
|
|
998
|
-
if (action === "list") {
|
|
999
|
-
const result = await listBudgets({
|
|
1000
|
-
page,
|
|
1001
|
-
perPage,
|
|
1002
|
-
additionalFilters: filter
|
|
1003
|
-
}, execCtx);
|
|
1004
|
-
return jsonResult(formatListResponse$1(result.data, formatBudget$1, result.meta, formatOptions));
|
|
1005
|
-
}
|
|
1006
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "budgets", VALID_ACTIONS$12));
|
|
1007
|
-
}
|
|
1008
|
-
/**
|
|
1009
|
-
* Comments MCP handler.
|
|
1010
|
-
*/
|
|
1011
|
-
var VALID_ACTIONS$11 = [
|
|
1012
|
-
"list",
|
|
1013
|
-
"get",
|
|
1014
|
-
"create",
|
|
1015
|
-
"update"
|
|
1016
|
-
];
|
|
1017
|
-
async function handleComments(action, args, ctx) {
|
|
1018
|
-
const { formatOptions, filter, page, perPage, include: userInclude } = ctx;
|
|
1019
|
-
const { id, body, task_id, deal_id, company_id } = args;
|
|
1020
|
-
const include = userInclude?.length ? [...new Set(["creator", ...userInclude])] : ["creator"];
|
|
1021
|
-
const execCtx = ctx.executor();
|
|
1022
|
-
if (action === "get") {
|
|
1023
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
|
|
1024
|
-
const result = await getComment({
|
|
1025
|
-
id,
|
|
1026
|
-
include
|
|
1027
|
-
}, execCtx);
|
|
1028
|
-
const formatted = formatComment$1(result.data, {
|
|
1029
|
-
...formatOptions,
|
|
1030
|
-
included: result.included
|
|
1031
|
-
});
|
|
1032
|
-
if (ctx.includeHints !== false) {
|
|
1033
|
-
const commentableType = result.data.attributes?.commentable_type;
|
|
1034
|
-
let commentableId;
|
|
1035
|
-
if (commentableType === "task") commentableId = result.data.relationships?.task?.data?.id;
|
|
1036
|
-
else if (commentableType === "deal") commentableId = result.data.relationships?.deal?.data?.id;
|
|
1037
|
-
else if (commentableType === "company") commentableId = result.data.relationships?.company?.data?.id;
|
|
1038
|
-
return jsonResult({
|
|
1039
|
-
...formatted,
|
|
1040
|
-
_hints: getCommentHints(id, commentableType, commentableId)
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
return jsonResult(formatted);
|
|
1044
|
-
}
|
|
1045
|
-
if (action === "create") {
|
|
1046
|
-
if (!body) return inputErrorResult(ErrorMessages.missingRequiredFields("comment", ["body"]));
|
|
1047
|
-
if (!task_id && !deal_id && !company_id) return inputErrorResult(ErrorMessages.missingCommentTarget());
|
|
1048
|
-
return jsonResult({
|
|
1049
|
-
success: true,
|
|
1050
|
-
...formatComment$1((await createComment({
|
|
1051
|
-
body,
|
|
1052
|
-
taskId: task_id,
|
|
1053
|
-
dealId: deal_id,
|
|
1054
|
-
companyId: company_id
|
|
1055
|
-
}, execCtx)).data, formatOptions)
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
if (action === "update") {
|
|
1059
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
|
|
1060
|
-
if (!body) return inputErrorResult(ErrorMessages.missingRequiredFields("comment update", ["body"]));
|
|
1061
|
-
return jsonResult({
|
|
1062
|
-
success: true,
|
|
1063
|
-
...formatComment$1((await updateComment({
|
|
1064
|
-
id,
|
|
1065
|
-
body
|
|
1066
|
-
}, execCtx)).data, formatOptions)
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
if (action === "list") {
|
|
1070
|
-
const result = await listComments({
|
|
1071
|
-
page,
|
|
1072
|
-
perPage,
|
|
1073
|
-
additionalFilters: filter,
|
|
1074
|
-
include
|
|
1075
|
-
}, execCtx);
|
|
1076
|
-
return jsonResult(formatListResponse$1(result.data, formatComment$1, result.meta, formatOptions));
|
|
1077
|
-
}
|
|
1078
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "comments", VALID_ACTIONS$11));
|
|
1079
|
-
}
|
|
1080
|
-
/**
|
|
1081
810
|
* Resolve handler for MCP.
|
|
1082
811
|
*
|
|
1083
812
|
* Thin wrapper around core's resource resolver.
|
|
@@ -1108,242 +837,403 @@ async function handleResolve(args, ctx) {
|
|
|
1108
837
|
}
|
|
1109
838
|
}
|
|
1110
839
|
/**
|
|
1111
|
-
*
|
|
840
|
+
* Merge user includes with defaults, ensuring no duplicates
|
|
1112
841
|
*/
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
"resolve"
|
|
1119
|
-
];
|
|
1120
|
-
async function handleCompanies(action, args, ctx) {
|
|
1121
|
-
const { formatOptions, filter, page, perPage } = ctx;
|
|
1122
|
-
const { id, name, query, type } = args;
|
|
1123
|
-
if (action === "resolve") return handleResolve({
|
|
1124
|
-
query,
|
|
1125
|
-
type
|
|
1126
|
-
}, ctx);
|
|
1127
|
-
const execCtx = ctx.executor();
|
|
1128
|
-
if (action === "get") {
|
|
1129
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
|
|
1130
|
-
const formatted = formatCompany$1((await getCompany({ id }, execCtx)).data, formatOptions);
|
|
1131
|
-
if (ctx.includeHints !== false) return jsonResult({
|
|
1132
|
-
...formatted,
|
|
1133
|
-
_hints: getCompanyHints(id)
|
|
1134
|
-
});
|
|
1135
|
-
return jsonResult(formatted);
|
|
1136
|
-
}
|
|
1137
|
-
if (action === "create") {
|
|
1138
|
-
if (!name) return inputErrorResult(ErrorMessages.missingRequiredFields("company", ["name"]));
|
|
1139
|
-
return jsonResult({
|
|
1140
|
-
success: true,
|
|
1141
|
-
...formatCompany$1((await createCompany({ name }, execCtx)).data, formatOptions)
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
if (action === "update") {
|
|
1145
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
|
|
1146
|
-
return jsonResult({
|
|
1147
|
-
success: true,
|
|
1148
|
-
...formatCompany$1((await updateCompany({
|
|
1149
|
-
id,
|
|
1150
|
-
name
|
|
1151
|
-
}, execCtx)).data, formatOptions)
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
if (action === "list") {
|
|
1155
|
-
const result = await listCompanies({
|
|
1156
|
-
page,
|
|
1157
|
-
perPage,
|
|
1158
|
-
additionalFilters: filter
|
|
1159
|
-
}, execCtx);
|
|
1160
|
-
const response = formatListResponse$1(result.data, formatCompany$1, result.meta, formatOptions);
|
|
1161
|
-
if (result.resolved && Object.keys(result.resolved).length > 0) return jsonResult({
|
|
1162
|
-
...response,
|
|
1163
|
-
_resolved: result.resolved
|
|
1164
|
-
});
|
|
1165
|
-
return jsonResult(response);
|
|
1166
|
-
}
|
|
1167
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "companies", VALID_ACTIONS$10));
|
|
842
|
+
function mergeIncludes(userInclude, defaults) {
|
|
843
|
+
if (!userInclude?.length && !defaults?.length) return void 0;
|
|
844
|
+
if (!userInclude?.length) return defaults;
|
|
845
|
+
if (!defaults?.length) return userInclude;
|
|
846
|
+
return [...new Set([...defaults, ...userInclude])];
|
|
1168
847
|
}
|
|
1169
848
|
/**
|
|
1170
|
-
*
|
|
849
|
+
* Create a resource handler function from configuration.
|
|
850
|
+
*
|
|
851
|
+
* @example
|
|
852
|
+
* ```typescript
|
|
853
|
+
* export const handleProjects = createResourceHandler({
|
|
854
|
+
* resource: 'projects',
|
|
855
|
+
* actions: ['list', 'get', 'resolve'],
|
|
856
|
+
* formatter: formatProject,
|
|
857
|
+
* hints: (data, id) => getProjectHints(id),
|
|
858
|
+
* supportsResolve: true,
|
|
859
|
+
* executors: {
|
|
860
|
+
* list: listProjects,
|
|
861
|
+
* get: getProject,
|
|
862
|
+
* },
|
|
863
|
+
* });
|
|
864
|
+
* ```
|
|
1171
865
|
*/
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
];
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
"company",
|
|
1193
|
-
"deal_status",
|
|
1194
|
-
"responsible",
|
|
1195
|
-
...userInclude
|
|
1196
|
-
])] : [
|
|
1197
|
-
"company",
|
|
1198
|
-
"deal_status",
|
|
1199
|
-
"responsible"
|
|
1200
|
-
]
|
|
1201
|
-
}, execCtx);
|
|
1202
|
-
const formatted = formatDeal$1(result.data, {
|
|
1203
|
-
...formatOptions,
|
|
1204
|
-
included: result.included
|
|
1205
|
-
});
|
|
1206
|
-
if (ctx.includeHints !== false) return jsonResult({
|
|
1207
|
-
...formatted,
|
|
1208
|
-
_hints: getDealHints(id)
|
|
1209
|
-
});
|
|
1210
|
-
return jsonResult(formatted);
|
|
1211
|
-
}
|
|
1212
|
-
if (action === "create") {
|
|
1213
|
-
if (!name || !company_id) return inputErrorResult(ErrorMessages.missingRequiredFields("deal", ["name", "company_id"]));
|
|
1214
|
-
return jsonResult({
|
|
1215
|
-
success: true,
|
|
1216
|
-
...formatDeal$1((await createDeal({
|
|
1217
|
-
name,
|
|
1218
|
-
companyId: company_id
|
|
1219
|
-
}, execCtx)).data, formatOptions)
|
|
1220
|
-
});
|
|
1221
|
-
}
|
|
1222
|
-
if (action === "update") {
|
|
1223
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
|
|
1224
|
-
return jsonResult({
|
|
1225
|
-
success: true,
|
|
1226
|
-
...formatDeal$1((await updateDeal({
|
|
866
|
+
function createResourceHandler(config) {
|
|
867
|
+
const { resource, displayName = resource, actions, formatter, hints, defaultInclude, supportsResolve, listFilterFromArgs, resolveArgsFromArgs, customActions, create: createConfig, update: updateConfig, executors } = config;
|
|
868
|
+
return async (action, args, ctx) => {
|
|
869
|
+
const { formatOptions, filter, page, perPage, include: userInclude } = ctx;
|
|
870
|
+
const { id, query, type } = args;
|
|
871
|
+
const execCtx = ctx.executor();
|
|
872
|
+
if (customActions?.[action]) return customActions[action](args, ctx, execCtx);
|
|
873
|
+
if (action === "resolve") {
|
|
874
|
+
if (!supportsResolve) return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));
|
|
875
|
+
return handleResolve({
|
|
876
|
+
query,
|
|
877
|
+
type,
|
|
878
|
+
...resolveArgsFromArgs?.(args)
|
|
879
|
+
}, ctx);
|
|
880
|
+
}
|
|
881
|
+
if (action === "get") {
|
|
882
|
+
if (!executors.get) return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));
|
|
883
|
+
if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
|
|
884
|
+
const include = mergeIncludes(userInclude, defaultInclude?.get);
|
|
885
|
+
const result = await executors.get({
|
|
1227
886
|
id,
|
|
1228
|
-
|
|
1229
|
-
}, execCtx)
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
887
|
+
include
|
|
888
|
+
}, execCtx);
|
|
889
|
+
const formatted = formatter(result.data, {
|
|
890
|
+
...formatOptions,
|
|
891
|
+
included: result.included
|
|
892
|
+
});
|
|
893
|
+
if (ctx.includeHints !== false && hints) return jsonResult({
|
|
894
|
+
...formatted,
|
|
895
|
+
_hints: hints(result.data, id)
|
|
896
|
+
});
|
|
897
|
+
return jsonResult(formatted);
|
|
898
|
+
}
|
|
899
|
+
if (action === "create") {
|
|
900
|
+
if (!executors.create || !createConfig) return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));
|
|
901
|
+
const missingFields = createConfig.required.filter((field) => !args[field]);
|
|
902
|
+
if (missingFields.length > 0) return inputErrorResult(ErrorMessages.missingRequiredFields(displayName, missingFields));
|
|
903
|
+
if (createConfig.validateArgs) {
|
|
904
|
+
const errorResult = createConfig.validateArgs(args);
|
|
905
|
+
if (errorResult) return errorResult;
|
|
906
|
+
}
|
|
907
|
+
const options = createConfig.mapOptions(args);
|
|
908
|
+
return jsonResult({
|
|
909
|
+
success: true,
|
|
910
|
+
...formatter((await executors.create(options, execCtx)).data, formatOptions)
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
if (action === "update") {
|
|
914
|
+
if (!executors.update || !updateConfig) return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));
|
|
915
|
+
if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
|
|
916
|
+
if (updateConfig.allowedFields && updateConfig.allowedFields.length > 0) {
|
|
917
|
+
if (!updateConfig.allowedFields.some((field) => args[field] !== void 0)) return inputErrorResult(ErrorMessages.noUpdateFieldsSpecified(updateConfig.allowedFields));
|
|
918
|
+
}
|
|
919
|
+
const options = {
|
|
920
|
+
id,
|
|
921
|
+
...updateConfig.mapOptions(args)
|
|
922
|
+
};
|
|
923
|
+
return jsonResult({
|
|
924
|
+
success: true,
|
|
925
|
+
...formatter((await executors.update(options, execCtx)).data, formatOptions)
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
if (action === "delete") {
|
|
929
|
+
if (!executors.delete) return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));
|
|
930
|
+
if (!id) return inputErrorResult(ErrorMessages.missingId("delete"));
|
|
931
|
+
await executors.delete({ id }, execCtx);
|
|
932
|
+
return jsonResult({
|
|
933
|
+
success: true,
|
|
934
|
+
deleted: id
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
if (action === "list") {
|
|
938
|
+
const include = mergeIncludes(userInclude, defaultInclude?.list);
|
|
939
|
+
const additionalFilters = {
|
|
940
|
+
...filter,
|
|
941
|
+
...listFilterFromArgs?.(args)
|
|
942
|
+
};
|
|
943
|
+
const result = await executors.list({
|
|
944
|
+
page,
|
|
945
|
+
perPage,
|
|
946
|
+
additionalFilters,
|
|
947
|
+
include
|
|
948
|
+
}, execCtx);
|
|
949
|
+
const response = formatListResponse$1(result.data, formatter, result.meta, {
|
|
950
|
+
...formatOptions,
|
|
951
|
+
included: result.included
|
|
952
|
+
});
|
|
953
|
+
if (result.resolved && Object.keys(result.resolved).length > 0) return jsonResult({
|
|
954
|
+
...response,
|
|
955
|
+
_resolved: result.resolved
|
|
956
|
+
});
|
|
957
|
+
return jsonResult(response);
|
|
958
|
+
}
|
|
959
|
+
return inputErrorResult(ErrorMessages.invalidAction(action, resource, actions));
|
|
960
|
+
};
|
|
1254
961
|
}
|
|
1255
962
|
/**
|
|
1256
|
-
*
|
|
963
|
+
* Attachments MCP handler.
|
|
1257
964
|
*/
|
|
1258
|
-
|
|
1259
|
-
"
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
if (
|
|
1273
|
-
|
|
1274
|
-
if (
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (!body || !page_id) return inputErrorResult(ErrorMessages.missingRequiredFields("discussion", ["body", "page_id"]));
|
|
1282
|
-
return jsonResult({
|
|
1283
|
-
success: true,
|
|
1284
|
-
...formatDiscussion$1((await createDiscussion({
|
|
1285
|
-
body,
|
|
1286
|
-
pageId: page_id,
|
|
1287
|
-
title
|
|
1288
|
-
}, execCtx)).data, formatOptions)
|
|
1289
|
-
});
|
|
965
|
+
const handleAttachments = createResourceHandler({
|
|
966
|
+
resource: "attachments",
|
|
967
|
+
actions: [
|
|
968
|
+
"list",
|
|
969
|
+
"get",
|
|
970
|
+
"delete"
|
|
971
|
+
],
|
|
972
|
+
formatter: formatAttachment$1,
|
|
973
|
+
hints: (data, id) => {
|
|
974
|
+
const attachableType = data.attributes?.attachable_type;
|
|
975
|
+
return getAttachmentHints(id, attachableType);
|
|
976
|
+
},
|
|
977
|
+
listFilterFromArgs: (args) => {
|
|
978
|
+
const filters = {};
|
|
979
|
+
if (args.task_id) filters.task_id = args.task_id;
|
|
980
|
+
if (args.comment_id) filters.comment_id = args.comment_id;
|
|
981
|
+
if (args.deal_id) filters.deal_id = args.deal_id;
|
|
982
|
+
return filters;
|
|
983
|
+
},
|
|
984
|
+
executors: {
|
|
985
|
+
list: listAttachments,
|
|
986
|
+
get: getAttachment,
|
|
987
|
+
delete: deleteAttachment
|
|
1290
988
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
989
|
+
});
|
|
990
|
+
/**
|
|
991
|
+
* Bookings MCP handler.
|
|
992
|
+
*/
|
|
993
|
+
const handleBookings = createResourceHandler({
|
|
994
|
+
resource: "bookings",
|
|
995
|
+
displayName: "booking",
|
|
996
|
+
actions: [
|
|
997
|
+
"list",
|
|
998
|
+
"get",
|
|
999
|
+
"create",
|
|
1000
|
+
"update"
|
|
1001
|
+
],
|
|
1002
|
+
formatter: formatBooking$1,
|
|
1003
|
+
hints: (data, id) => {
|
|
1004
|
+
const personId = data.relationships?.person?.data?.id;
|
|
1005
|
+
return getBookingHints(id, personId);
|
|
1006
|
+
},
|
|
1007
|
+
defaultInclude: {
|
|
1008
|
+
list: ["person", "service"],
|
|
1009
|
+
get: ["person", "service"]
|
|
1010
|
+
},
|
|
1011
|
+
create: {
|
|
1012
|
+
required: [
|
|
1013
|
+
"person_id",
|
|
1014
|
+
"started_on",
|
|
1015
|
+
"ended_on"
|
|
1016
|
+
],
|
|
1017
|
+
validateArgs: (args) => {
|
|
1018
|
+
if (!args.service_id && !args.event_id) return inputErrorResult(ErrorMessages.missingBookingTarget());
|
|
1019
|
+
},
|
|
1020
|
+
mapOptions: (args) => ({
|
|
1021
|
+
personId: args.person_id,
|
|
1022
|
+
serviceId: args.service_id ?? "",
|
|
1023
|
+
startedOn: args.started_on,
|
|
1024
|
+
endedOn: args.ended_on,
|
|
1025
|
+
time: args.time,
|
|
1026
|
+
note: args.note,
|
|
1027
|
+
eventId: args.event_id
|
|
1028
|
+
})
|
|
1029
|
+
},
|
|
1030
|
+
update: { mapOptions: (args) => ({
|
|
1031
|
+
startedOn: args.started_on,
|
|
1032
|
+
endedOn: args.ended_on,
|
|
1033
|
+
time: args.time,
|
|
1034
|
+
note: args.note
|
|
1035
|
+
}) },
|
|
1036
|
+
executors: {
|
|
1037
|
+
list: listBookings,
|
|
1038
|
+
get: getBooking,
|
|
1039
|
+
create: createBooking,
|
|
1040
|
+
update: updateBooking
|
|
1301
1041
|
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1042
|
+
});
|
|
1043
|
+
/**
|
|
1044
|
+
* Comments MCP handler.
|
|
1045
|
+
*/
|
|
1046
|
+
const handleComments = createResourceHandler({
|
|
1047
|
+
resource: "comments",
|
|
1048
|
+
actions: [
|
|
1049
|
+
"list",
|
|
1050
|
+
"get",
|
|
1051
|
+
"create",
|
|
1052
|
+
"update"
|
|
1053
|
+
],
|
|
1054
|
+
formatter: formatComment$1,
|
|
1055
|
+
hints: (data, id) => {
|
|
1056
|
+
const commentableType = data.attributes?.commentable_type;
|
|
1057
|
+
let commentableId;
|
|
1058
|
+
if (commentableType === "task") commentableId = data.relationships?.task?.data?.id;
|
|
1059
|
+
else if (commentableType === "deal") commentableId = data.relationships?.deal?.data?.id;
|
|
1060
|
+
else if (commentableType === "company") commentableId = data.relationships?.company?.data?.id;
|
|
1061
|
+
return getCommentHints(id, commentableType, commentableId);
|
|
1062
|
+
},
|
|
1063
|
+
defaultInclude: {
|
|
1064
|
+
list: ["creator"],
|
|
1065
|
+
get: ["creator"]
|
|
1066
|
+
},
|
|
1067
|
+
create: {
|
|
1068
|
+
required: ["body"],
|
|
1069
|
+
validateArgs: (args) => {
|
|
1070
|
+
if (!args.task_id && !args.deal_id && !args.company_id) return inputErrorResult(ErrorMessages.missingCommentTarget());
|
|
1071
|
+
},
|
|
1072
|
+
mapOptions: (args) => ({
|
|
1073
|
+
body: args.body,
|
|
1074
|
+
taskId: args.task_id,
|
|
1075
|
+
dealId: args.deal_id,
|
|
1076
|
+
companyId: args.company_id
|
|
1077
|
+
})
|
|
1078
|
+
},
|
|
1079
|
+
update: {
|
|
1080
|
+
allowedFields: ["body"],
|
|
1081
|
+
mapOptions: (args) => ({ body: args.body })
|
|
1082
|
+
},
|
|
1083
|
+
executors: {
|
|
1084
|
+
list: listComments,
|
|
1085
|
+
get: getComment,
|
|
1086
|
+
create: createComment,
|
|
1087
|
+
update: updateComment
|
|
1309
1088
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1089
|
+
});
|
|
1090
|
+
/**
|
|
1091
|
+
* Companies MCP handler.
|
|
1092
|
+
*
|
|
1093
|
+
* Uses the createResourceHandler factory for the common list/get/create/update/resolve pattern.
|
|
1094
|
+
*/
|
|
1095
|
+
/**
|
|
1096
|
+
* Handle companies resource.
|
|
1097
|
+
*
|
|
1098
|
+
* Supports: list, get, create, update, resolve
|
|
1099
|
+
*/
|
|
1100
|
+
const handleCompanies = createResourceHandler({
|
|
1101
|
+
resource: "companies",
|
|
1102
|
+
actions: [
|
|
1103
|
+
"list",
|
|
1104
|
+
"get",
|
|
1105
|
+
"create",
|
|
1106
|
+
"update",
|
|
1107
|
+
"resolve"
|
|
1108
|
+
],
|
|
1109
|
+
formatter: formatCompany$1,
|
|
1110
|
+
hints: (_data, id) => getCompanyHints(id),
|
|
1111
|
+
supportsResolve: true,
|
|
1112
|
+
create: {
|
|
1113
|
+
required: ["name"],
|
|
1114
|
+
mapOptions: (args) => ({ name: args.name })
|
|
1115
|
+
},
|
|
1116
|
+
update: { mapOptions: (args) => ({ name: args.name }) },
|
|
1117
|
+
executors: {
|
|
1118
|
+
list: listCompanies,
|
|
1119
|
+
get: getCompany,
|
|
1120
|
+
create: createCompany,
|
|
1121
|
+
update: updateCompany
|
|
1316
1122
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1123
|
+
});
|
|
1124
|
+
/**
|
|
1125
|
+
* Deals MCP handler.
|
|
1126
|
+
*/
|
|
1127
|
+
const handleDeals = createResourceHandler({
|
|
1128
|
+
resource: "deals",
|
|
1129
|
+
displayName: "deal",
|
|
1130
|
+
actions: [
|
|
1131
|
+
"list",
|
|
1132
|
+
"get",
|
|
1133
|
+
"create",
|
|
1134
|
+
"update",
|
|
1135
|
+
"resolve"
|
|
1136
|
+
],
|
|
1137
|
+
formatter: formatDeal$1,
|
|
1138
|
+
hints: (_data, id) => getDealHints(id),
|
|
1139
|
+
supportsResolve: true,
|
|
1140
|
+
defaultInclude: {
|
|
1141
|
+
list: ["company", "deal_status"],
|
|
1142
|
+
get: [
|
|
1143
|
+
"company",
|
|
1144
|
+
"deal_status",
|
|
1145
|
+
"responsible"
|
|
1146
|
+
]
|
|
1147
|
+
},
|
|
1148
|
+
create: {
|
|
1149
|
+
required: ["name", "company_id"],
|
|
1150
|
+
mapOptions: (args) => ({
|
|
1151
|
+
name: args.name,
|
|
1152
|
+
companyId: args.company_id
|
|
1153
|
+
})
|
|
1154
|
+
},
|
|
1155
|
+
update: { mapOptions: (args) => ({ name: args.name }) },
|
|
1156
|
+
executors: {
|
|
1157
|
+
list: listDeals,
|
|
1158
|
+
get: getDeal,
|
|
1159
|
+
create: createDeal,
|
|
1160
|
+
update: updateDeal
|
|
1323
1161
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1162
|
+
});
|
|
1163
|
+
/**
|
|
1164
|
+
* Discussions MCP handler.
|
|
1165
|
+
*/
|
|
1166
|
+
var STATUS_MAP = {
|
|
1167
|
+
active: "1",
|
|
1168
|
+
resolved: "2"
|
|
1169
|
+
};
|
|
1170
|
+
const handleDiscussions = createResourceHandler({
|
|
1171
|
+
resource: "discussions",
|
|
1172
|
+
actions: [
|
|
1173
|
+
"list",
|
|
1174
|
+
"get",
|
|
1175
|
+
"create",
|
|
1176
|
+
"update",
|
|
1177
|
+
"delete",
|
|
1178
|
+
"resolve",
|
|
1179
|
+
"reopen"
|
|
1180
|
+
],
|
|
1181
|
+
formatter: formatDiscussion$1,
|
|
1182
|
+
hints: (data, id) => {
|
|
1183
|
+
const pageId = data.relationships?.page?.data?.id;
|
|
1184
|
+
return getDiscussionHints(id, pageId);
|
|
1185
|
+
},
|
|
1186
|
+
listFilterFromArgs: (args) => {
|
|
1187
|
+
const filters = {};
|
|
1188
|
+
if (args.status) {
|
|
1189
|
+
const mapped = STATUS_MAP[args.status.toLowerCase()];
|
|
1190
|
+
if (mapped) filters.status = mapped;
|
|
1191
|
+
}
|
|
1192
|
+
return filters;
|
|
1193
|
+
},
|
|
1194
|
+
create: {
|
|
1195
|
+
required: ["body", "page_id"],
|
|
1196
|
+
mapOptions: (args) => ({
|
|
1197
|
+
body: args.body,
|
|
1198
|
+
pageId: args.page_id,
|
|
1199
|
+
title: args.title
|
|
1200
|
+
})
|
|
1201
|
+
},
|
|
1202
|
+
update: { mapOptions: (args) => ({
|
|
1203
|
+
title: args.title,
|
|
1204
|
+
body: args.body
|
|
1205
|
+
}) },
|
|
1206
|
+
customActions: {
|
|
1207
|
+
resolve: async (args, ctx, execCtx) => {
|
|
1208
|
+
if (!args.id) return inputErrorResult(ErrorMessages.missingId("resolve"));
|
|
1209
|
+
return jsonResult({
|
|
1210
|
+
success: true,
|
|
1211
|
+
...formatDiscussion$1((await resolveDiscussion({ id: args.id }, execCtx)).data, ctx.formatOptions)
|
|
1212
|
+
});
|
|
1213
|
+
},
|
|
1214
|
+
reopen: async (args, ctx, execCtx) => {
|
|
1215
|
+
if (!args.id) return inputErrorResult(ErrorMessages.missingId("reopen"));
|
|
1216
|
+
return jsonResult({
|
|
1217
|
+
success: true,
|
|
1218
|
+
...formatDiscussion$1((await reopenDiscussion({ id: args.id }, execCtx)).data, ctx.formatOptions)
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
},
|
|
1222
|
+
executors: {
|
|
1223
|
+
list: listDiscussions,
|
|
1224
|
+
get: getDiscussion,
|
|
1225
|
+
create: createDiscussion,
|
|
1226
|
+
update: updateDiscussion,
|
|
1227
|
+
delete: deleteDiscussion
|
|
1338
1228
|
}
|
|
1339
|
-
|
|
1340
|
-
}
|
|
1229
|
+
});
|
|
1341
1230
|
var RESOURCE_HELP = {
|
|
1342
1231
|
projects: {
|
|
1343
1232
|
description: "Manage projects in Productive.io",
|
|
1344
1233
|
actions: {
|
|
1345
1234
|
list: "List all projects with optional filters",
|
|
1346
|
-
get: "Get a single project by ID
|
|
1235
|
+
get: "Get a single project by ID (supports PRJ-123, P-123 format)",
|
|
1236
|
+
resolve: "Resolve by project number (PRJ-123, P-123)"
|
|
1347
1237
|
},
|
|
1348
1238
|
filters: {
|
|
1349
1239
|
query: "Text search on project name",
|
|
@@ -1393,7 +1283,8 @@ var RESOURCE_HELP = {
|
|
|
1393
1283
|
list: "List tasks with optional filters",
|
|
1394
1284
|
get: "Get a single task by ID with full details (description, comments, etc.)",
|
|
1395
1285
|
create: "Create a new task (requires title, project_id, task_list_id)",
|
|
1396
|
-
update: "Update an existing task"
|
|
1286
|
+
update: "Update an existing task",
|
|
1287
|
+
resolve: "Resolve by text search"
|
|
1397
1288
|
},
|
|
1398
1289
|
filters: {
|
|
1399
1290
|
query: "Text search on task title",
|
|
@@ -1478,7 +1369,9 @@ var RESOURCE_HELP = {
|
|
|
1478
1369
|
list: "List time entries with optional filters",
|
|
1479
1370
|
get: "Get a single time entry by ID",
|
|
1480
1371
|
create: "Create a new time entry (requires person_id, service_id, date, time)",
|
|
1481
|
-
update: "Update an existing time entry"
|
|
1372
|
+
update: "Update an existing time entry",
|
|
1373
|
+
delete: "Delete a time entry",
|
|
1374
|
+
resolve: "Resolve related resources (person, project, service)"
|
|
1482
1375
|
},
|
|
1483
1376
|
filters: {
|
|
1484
1377
|
person_id: "Filter by person (use \"me\" for current user)",
|
|
@@ -1559,8 +1452,9 @@ var RESOURCE_HELP = {
|
|
|
1559
1452
|
description: "Team members and contacts",
|
|
1560
1453
|
actions: {
|
|
1561
1454
|
list: "List people with optional filters",
|
|
1562
|
-
get: "Get a single person by ID",
|
|
1563
|
-
me: "Get the currently authenticated user"
|
|
1455
|
+
get: "Get a single person by ID (supports email address)",
|
|
1456
|
+
me: "Get the currently authenticated user",
|
|
1457
|
+
resolve: "Resolve by email address"
|
|
1564
1458
|
},
|
|
1565
1459
|
filters: {
|
|
1566
1460
|
query: "Text search on name or email",
|
|
@@ -1610,9 +1504,10 @@ var RESOURCE_HELP = {
|
|
|
1610
1504
|
description: "Client companies and organizations",
|
|
1611
1505
|
actions: {
|
|
1612
1506
|
list: "List companies with optional filters",
|
|
1613
|
-
get: "Get a single company by ID",
|
|
1507
|
+
get: "Get a single company by ID (supports company name)",
|
|
1614
1508
|
create: "Create a new company (requires name)",
|
|
1615
|
-
update: "Update an existing company"
|
|
1509
|
+
update: "Update an existing company",
|
|
1510
|
+
resolve: "Resolve by company name"
|
|
1616
1511
|
},
|
|
1617
1512
|
filters: {
|
|
1618
1513
|
query: "Text search on company name",
|
|
@@ -1776,12 +1671,13 @@ var RESOURCE_HELP = {
|
|
|
1776
1671
|
]
|
|
1777
1672
|
},
|
|
1778
1673
|
deals: {
|
|
1779
|
-
description: "Sales deals and
|
|
1674
|
+
description: "Sales deals, opportunities, and budgets. Budgets are deals with budget=true — use filter[type]=2 to list only budgets.",
|
|
1780
1675
|
actions: {
|
|
1781
1676
|
list: "List deals with optional filters",
|
|
1782
|
-
get: "Get a single deal by ID",
|
|
1677
|
+
get: "Get a single deal by ID (supports D-123, DEAL-123 format)",
|
|
1783
1678
|
create: "Create a new deal (requires name, company_id)",
|
|
1784
|
-
update: "Update an existing deal"
|
|
1679
|
+
update: "Update an existing deal",
|
|
1680
|
+
resolve: "Resolve by deal number (D-123, DEAL-123)"
|
|
1785
1681
|
},
|
|
1786
1682
|
filters: {
|
|
1787
1683
|
query: "Text search on deal name",
|
|
@@ -1804,23 +1700,35 @@ var RESOURCE_HELP = {
|
|
|
1804
1700
|
name: "Deal name",
|
|
1805
1701
|
number: "Deal number",
|
|
1806
1702
|
date: "Deal date",
|
|
1703
|
+
budget: "Whether this deal is a budget (true/false)",
|
|
1807
1704
|
status: "Current status (from deal_status)"
|
|
1808
1705
|
},
|
|
1809
|
-
examples: [
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1706
|
+
examples: [
|
|
1707
|
+
{
|
|
1708
|
+
description: "Search deals",
|
|
1709
|
+
params: {
|
|
1710
|
+
resource: "deals",
|
|
1711
|
+
action: "list",
|
|
1712
|
+
query: "website redesign"
|
|
1713
|
+
}
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
description: "List deals for a company",
|
|
1717
|
+
params: {
|
|
1718
|
+
resource: "deals",
|
|
1719
|
+
action: "list",
|
|
1720
|
+
filter: { company_id: "12345" }
|
|
1721
|
+
}
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
description: "List only budgets",
|
|
1725
|
+
params: {
|
|
1726
|
+
resource: "deals",
|
|
1727
|
+
action: "list",
|
|
1728
|
+
filter: { type: "2" }
|
|
1729
|
+
}
|
|
1822
1730
|
}
|
|
1823
|
-
|
|
1731
|
+
]
|
|
1824
1732
|
},
|
|
1825
1733
|
bookings: {
|
|
1826
1734
|
description: "Resource scheduling and capacity planning",
|
|
@@ -1863,66 +1771,6 @@ var RESOURCE_HELP = {
|
|
|
1863
1771
|
}
|
|
1864
1772
|
}]
|
|
1865
1773
|
},
|
|
1866
|
-
budgets: {
|
|
1867
|
-
description: "Budget tracking and financial overview",
|
|
1868
|
-
actions: {
|
|
1869
|
-
list: "List budgets with optional filters",
|
|
1870
|
-
get: "Get a single budget by ID with full details"
|
|
1871
|
-
},
|
|
1872
|
-
filters: {
|
|
1873
|
-
project_id: "Filter by project",
|
|
1874
|
-
company_id: "Filter by company",
|
|
1875
|
-
deal_id: "Filter by deal",
|
|
1876
|
-
billable: "Filter by billable status (true/false)",
|
|
1877
|
-
budget_type: "Filter by budget type"
|
|
1878
|
-
},
|
|
1879
|
-
fields: {
|
|
1880
|
-
id: "Unique budget identifier",
|
|
1881
|
-
name: "Budget name",
|
|
1882
|
-
budget_type: "Type of budget",
|
|
1883
|
-
billable: "Whether the budget is billable",
|
|
1884
|
-
started_on: "Budget start date (YYYY-MM-DD)",
|
|
1885
|
-
ended_on: "Budget end date (YYYY-MM-DD)",
|
|
1886
|
-
currency: "Budget currency code",
|
|
1887
|
-
total_time_budget: "Total time budget in minutes",
|
|
1888
|
-
remaining_time_budget: "Remaining time budget in minutes",
|
|
1889
|
-
total_monetary_budget: "Total monetary budget",
|
|
1890
|
-
remaining_monetary_budget: "Remaining monetary budget"
|
|
1891
|
-
},
|
|
1892
|
-
examples: [
|
|
1893
|
-
{
|
|
1894
|
-
description: "List all budgets",
|
|
1895
|
-
params: {
|
|
1896
|
-
resource: "budgets",
|
|
1897
|
-
action: "list"
|
|
1898
|
-
}
|
|
1899
|
-
},
|
|
1900
|
-
{
|
|
1901
|
-
description: "List budgets for a project",
|
|
1902
|
-
params: {
|
|
1903
|
-
resource: "budgets",
|
|
1904
|
-
action: "list",
|
|
1905
|
-
filter: { project_id: "12345" }
|
|
1906
|
-
}
|
|
1907
|
-
},
|
|
1908
|
-
{
|
|
1909
|
-
description: "Get budget details",
|
|
1910
|
-
params: {
|
|
1911
|
-
resource: "budgets",
|
|
1912
|
-
action: "get",
|
|
1913
|
-
id: "67890"
|
|
1914
|
-
}
|
|
1915
|
-
},
|
|
1916
|
-
{
|
|
1917
|
-
description: "List billable budgets",
|
|
1918
|
-
params: {
|
|
1919
|
-
resource: "budgets",
|
|
1920
|
-
action: "list",
|
|
1921
|
-
filter: { billable: "true" }
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
]
|
|
1925
|
-
},
|
|
1926
1774
|
pages: {
|
|
1927
1775
|
description: "Manage pages (wiki/docs) within projects",
|
|
1928
1776
|
actions: {
|
|
@@ -2123,77 +1971,51 @@ function handleHelpOverview() {
|
|
|
2123
1971
|
}
|
|
2124
1972
|
/**
|
|
2125
1973
|
* Pages MCP handler.
|
|
1974
|
+
*
|
|
1975
|
+
* Uses the createResourceHandler factory for the common list/get/create/update/delete pattern.
|
|
2126
1976
|
*/
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
...formatPage$1((await updatePage({
|
|
2164
|
-
id,
|
|
2165
|
-
title,
|
|
2166
|
-
body
|
|
2167
|
-
}, execCtx)).data, formatOptions)
|
|
2168
|
-
});
|
|
2169
|
-
}
|
|
2170
|
-
if (action === "delete") {
|
|
2171
|
-
if (!id) return inputErrorResult(ErrorMessages.missingId("delete"));
|
|
2172
|
-
await deletePage({ id }, execCtx);
|
|
2173
|
-
return jsonResult({
|
|
2174
|
-
success: true,
|
|
2175
|
-
deleted: id
|
|
2176
|
-
});
|
|
2177
|
-
}
|
|
2178
|
-
if (action === "list") {
|
|
2179
|
-
const result = await listPages({
|
|
2180
|
-
page,
|
|
2181
|
-
perPage,
|
|
2182
|
-
additionalFilters: filter
|
|
2183
|
-
}, execCtx);
|
|
2184
|
-
const response = formatListResponse$1(result.data, formatPage$1, result.meta, formatOptions);
|
|
2185
|
-
if (result.resolved && Object.keys(result.resolved).length > 0) return jsonResult({
|
|
2186
|
-
...response,
|
|
2187
|
-
_resolved: result.resolved
|
|
2188
|
-
});
|
|
2189
|
-
return jsonResult(response);
|
|
1977
|
+
/**
|
|
1978
|
+
* Handle pages resource.
|
|
1979
|
+
*
|
|
1980
|
+
* Supports: list, get, create, update, delete
|
|
1981
|
+
*/
|
|
1982
|
+
const handlePages = createResourceHandler({
|
|
1983
|
+
resource: "pages",
|
|
1984
|
+
actions: [
|
|
1985
|
+
"list",
|
|
1986
|
+
"get",
|
|
1987
|
+
"create",
|
|
1988
|
+
"update",
|
|
1989
|
+
"delete"
|
|
1990
|
+
],
|
|
1991
|
+
formatter: formatPage$1,
|
|
1992
|
+
hints: (_data, id) => getPageHints(id),
|
|
1993
|
+
supportsResolve: false,
|
|
1994
|
+
create: {
|
|
1995
|
+
required: ["title", "project_id"],
|
|
1996
|
+
mapOptions: (args) => ({
|
|
1997
|
+
title: args.title,
|
|
1998
|
+
projectId: args.project_id,
|
|
1999
|
+
body: args.body,
|
|
2000
|
+
parentPageId: args.parent_page_id
|
|
2001
|
+
})
|
|
2002
|
+
},
|
|
2003
|
+
update: { mapOptions: (args) => ({
|
|
2004
|
+
title: args.title,
|
|
2005
|
+
body: args.body
|
|
2006
|
+
}) },
|
|
2007
|
+
executors: {
|
|
2008
|
+
list: listPages,
|
|
2009
|
+
get: getPage,
|
|
2010
|
+
create: createPage,
|
|
2011
|
+
update: updatePage,
|
|
2012
|
+
delete: deletePage
|
|
2190
2013
|
}
|
|
2191
|
-
|
|
2192
|
-
}
|
|
2014
|
+
});
|
|
2193
2015
|
/**
|
|
2194
2016
|
* People MCP handler.
|
|
2195
2017
|
*/
|
|
2196
|
-
var VALID_ACTIONS$
|
|
2018
|
+
var VALID_ACTIONS$1 = [
|
|
2197
2019
|
"list",
|
|
2198
2020
|
"get",
|
|
2199
2021
|
"me",
|
|
@@ -2244,48 +2066,33 @@ async function handlePeople(action, args, ctx, credentials) {
|
|
|
2244
2066
|
});
|
|
2245
2067
|
return jsonResult(response);
|
|
2246
2068
|
}
|
|
2247
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "people", VALID_ACTIONS$
|
|
2069
|
+
return inputErrorResult(ErrorMessages.invalidAction(action, "people", VALID_ACTIONS$1));
|
|
2248
2070
|
}
|
|
2249
2071
|
/**
|
|
2250
2072
|
* Projects MCP handler.
|
|
2073
|
+
*
|
|
2074
|
+
* Uses the createResourceHandler factory for the common list/get/resolve pattern.
|
|
2251
2075
|
*/
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
_hints: getProjectHints(id)
|
|
2271
|
-
});
|
|
2272
|
-
return jsonResult(formatted);
|
|
2273
|
-
}
|
|
2274
|
-
if (action === "list") {
|
|
2275
|
-
const result = await listProjects({
|
|
2276
|
-
page,
|
|
2277
|
-
perPage,
|
|
2278
|
-
additionalFilters: filter
|
|
2279
|
-
}, execCtx);
|
|
2280
|
-
const response = formatListResponse$1(result.data, formatProject$1, result.meta, formatOptions);
|
|
2281
|
-
if (result.resolved && Object.keys(result.resolved).length > 0) return jsonResult({
|
|
2282
|
-
...response,
|
|
2283
|
-
_resolved: result.resolved
|
|
2284
|
-
});
|
|
2285
|
-
return jsonResult(response);
|
|
2076
|
+
/**
|
|
2077
|
+
* Handle projects resource.
|
|
2078
|
+
*
|
|
2079
|
+
* Supports: list, get, resolve
|
|
2080
|
+
*/
|
|
2081
|
+
const handleProjects = createResourceHandler({
|
|
2082
|
+
resource: "projects",
|
|
2083
|
+
actions: [
|
|
2084
|
+
"list",
|
|
2085
|
+
"get",
|
|
2086
|
+
"resolve"
|
|
2087
|
+
],
|
|
2088
|
+
formatter: formatProject$1,
|
|
2089
|
+
hints: (_data, id) => getProjectHints(id),
|
|
2090
|
+
supportsResolve: true,
|
|
2091
|
+
executors: {
|
|
2092
|
+
list: listProjects,
|
|
2093
|
+
get: getProject
|
|
2286
2094
|
}
|
|
2287
|
-
|
|
2288
|
-
}
|
|
2095
|
+
});
|
|
2289
2096
|
/**
|
|
2290
2097
|
* Reports MCP handler.
|
|
2291
2098
|
*/
|
|
@@ -2299,11 +2106,11 @@ function formatReportData(data) {
|
|
|
2299
2106
|
};
|
|
2300
2107
|
});
|
|
2301
2108
|
}
|
|
2302
|
-
var VALID_ACTIONS
|
|
2109
|
+
var VALID_ACTIONS = ["get"];
|
|
2303
2110
|
async function handleReports(action, args, ctx) {
|
|
2304
2111
|
const { filter, page, perPage } = ctx;
|
|
2305
2112
|
const { report_type, group, from, to, person_id, project_id, company_id, deal_id, status } = args;
|
|
2306
|
-
if (action !== "get") return inputErrorResult(ErrorMessages.invalidAction(action, "reports", VALID_ACTIONS
|
|
2113
|
+
if (action !== "get") return inputErrorResult(ErrorMessages.invalidAction(action, "reports", VALID_ACTIONS));
|
|
2307
2114
|
if (!report_type) return inputErrorResult(ErrorMessages.missingReportType());
|
|
2308
2115
|
if (!VALID_REPORT_TYPES.includes(report_type)) return inputErrorResult(ErrorMessages.invalidReportType(report_type, [...VALID_REPORT_TYPES]));
|
|
2309
2116
|
const execCtx = ctx.executor();
|
|
@@ -2328,244 +2135,171 @@ async function handleReports(action, args, ctx) {
|
|
|
2328
2135
|
}
|
|
2329
2136
|
/**
|
|
2330
2137
|
* Services MCP handler.
|
|
2138
|
+
*
|
|
2139
|
+
* Uses the createResourceHandler factory for the common list pattern.
|
|
2331
2140
|
*/
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
}
|
|
2344
|
-
return inputErrorResult(ErrorMessages.invalidAction(action, "services", VALID_ACTIONS$3));
|
|
2345
|
-
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Handle services resource.
|
|
2143
|
+
*
|
|
2144
|
+
* Supports: list
|
|
2145
|
+
*/
|
|
2146
|
+
const handleServices = createResourceHandler({
|
|
2147
|
+
resource: "services",
|
|
2148
|
+
actions: ["list"],
|
|
2149
|
+
formatter: formatService$1,
|
|
2150
|
+
executors: { list: listServices }
|
|
2151
|
+
});
|
|
2346
2152
|
/**
|
|
2347
2153
|
* Tasks MCP handler.
|
|
2348
2154
|
*/
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
"
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
},
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
}, execCtx);
|
|
2373
|
-
const formatted = formatTask$1(result.data, {
|
|
2374
|
-
...formatOptions,
|
|
2375
|
-
included: result.included
|
|
2376
|
-
});
|
|
2377
|
-
if (ctx.includeHints !== false) {
|
|
2378
|
-
const serviceId = result.data.relationships?.service?.data?.id;
|
|
2379
|
-
return jsonResult({
|
|
2380
|
-
...formatted,
|
|
2381
|
-
_hints: getTaskHints(id, serviceId)
|
|
2382
|
-
});
|
|
2383
|
-
}
|
|
2384
|
-
return jsonResult(formatted);
|
|
2385
|
-
}
|
|
2386
|
-
if (action === "create") {
|
|
2387
|
-
if (!title || !project_id || !task_list_id) return inputErrorResult(ErrorMessages.missingRequiredFields("task", [
|
|
2155
|
+
const handleTasks = createResourceHandler({
|
|
2156
|
+
resource: "tasks",
|
|
2157
|
+
displayName: "task",
|
|
2158
|
+
actions: [
|
|
2159
|
+
"list",
|
|
2160
|
+
"get",
|
|
2161
|
+
"create",
|
|
2162
|
+
"update",
|
|
2163
|
+
"resolve"
|
|
2164
|
+
],
|
|
2165
|
+
formatter: formatTask$1,
|
|
2166
|
+
hints: (data, id) => {
|
|
2167
|
+
const serviceId = data.relationships?.service?.data?.id;
|
|
2168
|
+
return getTaskHints(id, serviceId);
|
|
2169
|
+
},
|
|
2170
|
+
supportsResolve: true,
|
|
2171
|
+
resolveArgsFromArgs: (args) => ({ project_id: args.project_id }),
|
|
2172
|
+
defaultInclude: {
|
|
2173
|
+
list: ["project", "project.company"],
|
|
2174
|
+
get: ["project", "project.company"]
|
|
2175
|
+
},
|
|
2176
|
+
create: {
|
|
2177
|
+
required: [
|
|
2388
2178
|
"title",
|
|
2389
2179
|
"project_id",
|
|
2390
2180
|
"task_list_id"
|
|
2391
|
-
]
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
description,
|
|
2411
|
-
assigneeId: assignee_id
|
|
2412
|
-
}, execCtx)).data, formatOptions)
|
|
2413
|
-
});
|
|
2414
|
-
}
|
|
2415
|
-
if (action === "list") {
|
|
2416
|
-
const result = await listTasks({
|
|
2417
|
-
page,
|
|
2418
|
-
perPage,
|
|
2419
|
-
additionalFilters: filter,
|
|
2420
|
-
include
|
|
2421
|
-
}, execCtx);
|
|
2422
|
-
const response = formatListResponse$1(result.data, formatTask$1, result.meta, {
|
|
2423
|
-
...formatOptions,
|
|
2424
|
-
included: result.included
|
|
2425
|
-
});
|
|
2426
|
-
if (result.resolved && Object.keys(result.resolved).length > 0) return jsonResult({
|
|
2427
|
-
...response,
|
|
2428
|
-
_resolved: result.resolved
|
|
2429
|
-
});
|
|
2430
|
-
return jsonResult(response);
|
|
2181
|
+
],
|
|
2182
|
+
mapOptions: (args) => ({
|
|
2183
|
+
title: args.title,
|
|
2184
|
+
projectId: args.project_id,
|
|
2185
|
+
taskListId: args.task_list_id,
|
|
2186
|
+
assigneeId: args.assignee_id,
|
|
2187
|
+
description: args.description
|
|
2188
|
+
})
|
|
2189
|
+
},
|
|
2190
|
+
update: { mapOptions: (args) => ({
|
|
2191
|
+
title: args.title,
|
|
2192
|
+
description: args.description,
|
|
2193
|
+
assigneeId: args.assignee_id
|
|
2194
|
+
}) },
|
|
2195
|
+
executors: {
|
|
2196
|
+
list: listTasks,
|
|
2197
|
+
get: getTask,
|
|
2198
|
+
create: createTask,
|
|
2199
|
+
update: updateTask
|
|
2431
2200
|
}
|
|
2432
|
-
|
|
2433
|
-
}
|
|
2201
|
+
});
|
|
2434
2202
|
/**
|
|
2435
2203
|
* Time entries MCP handler.
|
|
2436
2204
|
*
|
|
2437
2205
|
* Thin adapter that delegates business logic to core executors
|
|
2438
2206
|
* and handles MCP-specific concerns (hints, error formatting, JSON results).
|
|
2439
2207
|
*/
|
|
2440
|
-
|
|
2441
|
-
"
|
|
2442
|
-
"
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
if (ctx.includeHints !== false) {
|
|
2461
|
-
const serviceId = result.data.relationships?.service?.data?.id;
|
|
2462
|
-
return jsonResult({
|
|
2463
|
-
...formatted,
|
|
2464
|
-
_hints: getTimeEntryHints(id, void 0, serviceId)
|
|
2465
|
-
});
|
|
2466
|
-
}
|
|
2467
|
-
return jsonResult(formatted);
|
|
2468
|
-
}
|
|
2469
|
-
if (action === "create") {
|
|
2470
|
-
if (!person_id || !service_id || !time || !date) return inputErrorResult(ErrorMessages.missingRequiredFields("time entry", [
|
|
2208
|
+
const handleTime = createResourceHandler({
|
|
2209
|
+
resource: "time",
|
|
2210
|
+
displayName: "time entry",
|
|
2211
|
+
actions: [
|
|
2212
|
+
"list",
|
|
2213
|
+
"get",
|
|
2214
|
+
"create",
|
|
2215
|
+
"update",
|
|
2216
|
+
"delete",
|
|
2217
|
+
"resolve"
|
|
2218
|
+
],
|
|
2219
|
+
formatter: formatTimeEntry$1,
|
|
2220
|
+
hints: (data, id) => {
|
|
2221
|
+
const serviceId = data.relationships?.service?.data?.id;
|
|
2222
|
+
return getTimeEntryHints(id, void 0, serviceId);
|
|
2223
|
+
},
|
|
2224
|
+
supportsResolve: true,
|
|
2225
|
+
resolveArgsFromArgs: (args) => ({ project_id: args.project_id }),
|
|
2226
|
+
create: {
|
|
2227
|
+
required: [
|
|
2471
2228
|
"person_id",
|
|
2472
2229
|
"service_id",
|
|
2473
2230
|
"time",
|
|
2474
2231
|
"date"
|
|
2475
|
-
]
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
note: note ?? void 0
|
|
2498
|
-
}, execCtx)).data, formatOptions)
|
|
2499
|
-
});
|
|
2500
|
-
}
|
|
2501
|
-
if (action === "list") {
|
|
2502
|
-
const result = await listTimeEntries({
|
|
2503
|
-
page,
|
|
2504
|
-
perPage,
|
|
2505
|
-
additionalFilters: filter
|
|
2506
|
-
}, execCtx);
|
|
2507
|
-
const response = formatListResponse$1(result.data, formatTimeEntry$1, result.meta, formatOptions);
|
|
2508
|
-
if (result.resolved && Object.keys(result.resolved).length > 0) return jsonResult({
|
|
2509
|
-
...response,
|
|
2510
|
-
_resolved: result.resolved
|
|
2511
|
-
});
|
|
2512
|
-
return jsonResult(response);
|
|
2232
|
+
],
|
|
2233
|
+
mapOptions: (args) => ({
|
|
2234
|
+
personId: args.person_id,
|
|
2235
|
+
serviceId: args.service_id,
|
|
2236
|
+
time: args.time,
|
|
2237
|
+
date: args.date,
|
|
2238
|
+
note: args.note ?? void 0,
|
|
2239
|
+
taskId: args.task_id,
|
|
2240
|
+
projectId: args.project_id
|
|
2241
|
+
})
|
|
2242
|
+
},
|
|
2243
|
+
update: { mapOptions: (args) => ({
|
|
2244
|
+
time: args.time ?? void 0,
|
|
2245
|
+
date: args.date ?? void 0,
|
|
2246
|
+
note: args.note ?? void 0
|
|
2247
|
+
}) },
|
|
2248
|
+
executors: {
|
|
2249
|
+
list: listTimeEntries,
|
|
2250
|
+
get: getTimeEntry,
|
|
2251
|
+
create: createTimeEntry,
|
|
2252
|
+
update: updateTimeEntry,
|
|
2253
|
+
delete: deleteTimeEntry
|
|
2513
2254
|
}
|
|
2514
|
-
|
|
2515
|
-
}
|
|
2255
|
+
});
|
|
2516
2256
|
/**
|
|
2517
2257
|
* Timers MCP handler.
|
|
2518
2258
|
*/
|
|
2519
|
-
|
|
2520
|
-
"
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
}
|
|
2549
|
-
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
perPage,
|
|
2562
|
-
additionalFilters: filter,
|
|
2563
|
-
include
|
|
2564
|
-
}, execCtx);
|
|
2565
|
-
return jsonResult(formatListResponse$1(result.data, formatTimer$1, result.meta, formatOptions));
|
|
2259
|
+
const handleTimers = createResourceHandler({
|
|
2260
|
+
resource: "timers",
|
|
2261
|
+
actions: [
|
|
2262
|
+
"list",
|
|
2263
|
+
"get",
|
|
2264
|
+
"start",
|
|
2265
|
+
"stop"
|
|
2266
|
+
],
|
|
2267
|
+
formatter: formatTimer$1,
|
|
2268
|
+
hints: (_data, id) => getTimerHints(id),
|
|
2269
|
+
customActions: {
|
|
2270
|
+
start: async (args, ctx, execCtx) => {
|
|
2271
|
+
if (!args.service_id && !args.time_entry_id) return inputErrorResult(ErrorMessages.missingServiceForTimer());
|
|
2272
|
+
return jsonResult({
|
|
2273
|
+
success: true,
|
|
2274
|
+
...formatTimer$1((await startTimer({
|
|
2275
|
+
serviceId: args.service_id,
|
|
2276
|
+
timeEntryId: args.time_entry_id
|
|
2277
|
+
}, execCtx)).data, ctx.formatOptions)
|
|
2278
|
+
});
|
|
2279
|
+
},
|
|
2280
|
+
create: async (args, ctx, execCtx) => {
|
|
2281
|
+
if (!args.service_id && !args.time_entry_id) return inputErrorResult(ErrorMessages.missingServiceForTimer());
|
|
2282
|
+
return jsonResult({
|
|
2283
|
+
success: true,
|
|
2284
|
+
...formatTimer$1((await startTimer({
|
|
2285
|
+
serviceId: args.service_id,
|
|
2286
|
+
timeEntryId: args.time_entry_id
|
|
2287
|
+
}, execCtx)).data, ctx.formatOptions)
|
|
2288
|
+
});
|
|
2289
|
+
},
|
|
2290
|
+
stop: async (args, ctx, execCtx) => {
|
|
2291
|
+
if (!args.id) return inputErrorResult(ErrorMessages.missingId("stop"));
|
|
2292
|
+
return jsonResult({
|
|
2293
|
+
success: true,
|
|
2294
|
+
...formatTimer$1((await stopTimer({ id: args.id }, execCtx)).data, ctx.formatOptions)
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
},
|
|
2298
|
+
executors: {
|
|
2299
|
+
list: listTimers,
|
|
2300
|
+
get: getTimer
|
|
2566
2301
|
}
|
|
2567
|
-
|
|
2568
|
-
}
|
|
2302
|
+
});
|
|
2569
2303
|
/**
|
|
2570
2304
|
* Tool execution handlers for Productive MCP server
|
|
2571
2305
|
* These are shared between stdio and HTTP transports
|
|
@@ -2584,7 +2318,8 @@ async function executeToolWithCredentials(name, args, credentials) {
|
|
|
2584
2318
|
const api = new ProductiveApi({ config: {
|
|
2585
2319
|
apiToken: credentials.apiToken,
|
|
2586
2320
|
organizationId: credentials.organizationId,
|
|
2587
|
-
userId: credentials.userId
|
|
2321
|
+
userId: credentials.userId,
|
|
2322
|
+
baseUrl: process.env.PRODUCTIVE_BASE_URL
|
|
2588
2323
|
} });
|
|
2589
2324
|
if (name !== "productive") return errorResult(`Unknown tool: ${name}`);
|
|
2590
2325
|
const { resource, action, filter, page, per_page, compact, include, query, no_hints, type, ...restArgs } = args;
|
|
@@ -2643,10 +2378,14 @@ async function executeToolWithCredentials(name, args, credentials) {
|
|
|
2643
2378
|
...resolveArgs
|
|
2644
2379
|
}, ctx);
|
|
2645
2380
|
case "bookings": return await handleBookings(action, restArgs, ctx);
|
|
2646
|
-
case "budgets": return await handleBudgets(action, restArgs, ctx);
|
|
2647
2381
|
case "pages": return await handlePages(action, restArgs, ctx);
|
|
2648
2382
|
case "discussions": return await handleDiscussions(action, restArgs, ctx);
|
|
2649
2383
|
case "reports": return await handleReports(action, restArgs, ctx);
|
|
2384
|
+
case "budgets": return inputErrorResult(new UserInputError("The \"budgets\" resource has been removed. Budgets are deals with type=2.", [
|
|
2385
|
+
"Use resource=\"deals\" with filter[type]=\"2\" to list only budgets",
|
|
2386
|
+
"To create a budget: resource=\"deals\" action=\"create\" with budget=true",
|
|
2387
|
+
"Use action=\"help\" resource=\"deals\" for full documentation"
|
|
2388
|
+
]));
|
|
2650
2389
|
default: return inputErrorResult(ErrorMessages.unknownResource(resource, VALID_RESOURCES));
|
|
2651
2390
|
}
|
|
2652
2391
|
} catch (error) {
|
|
@@ -2662,4 +2401,4 @@ async function executeToolWithCredentials(name, args, credentials) {
|
|
|
2662
2401
|
}
|
|
2663
2402
|
export { executeToolWithCredentials as t };
|
|
2664
2403
|
|
|
2665
|
-
//# sourceMappingURL=handlers-
|
|
2404
|
+
//# sourceMappingURL=handlers-BE3CYyVX.js.map
|