@studiometa/productive-mcp 0.7.0 → 0.8.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.
Files changed (52) hide show
  1. package/dist/errors.d.ts +41 -0
  2. package/dist/errors.d.ts.map +1 -0
  3. package/dist/handlers/bookings.d.ts +1 -1
  4. package/dist/handlers/bookings.d.ts.map +1 -1
  5. package/dist/handlers/comments.d.ts +1 -1
  6. package/dist/handlers/comments.d.ts.map +1 -1
  7. package/dist/handlers/companies.d.ts +1 -1
  8. package/dist/handlers/companies.d.ts.map +1 -1
  9. package/dist/handlers/deals.d.ts +1 -1
  10. package/dist/handlers/deals.d.ts.map +1 -1
  11. package/dist/handlers/index.d.ts.map +1 -1
  12. package/dist/handlers/people.d.ts +1 -1
  13. package/dist/handlers/people.d.ts.map +1 -1
  14. package/dist/handlers/projects.d.ts +1 -1
  15. package/dist/handlers/projects.d.ts.map +1 -1
  16. package/dist/handlers/reports.d.ts +1 -1
  17. package/dist/handlers/reports.d.ts.map +1 -1
  18. package/dist/handlers/services.d.ts +1 -1
  19. package/dist/handlers/services.d.ts.map +1 -1
  20. package/dist/handlers/tasks.d.ts.map +1 -1
  21. package/dist/handlers/time.d.ts +1 -1
  22. package/dist/handlers/time.d.ts.map +1 -1
  23. package/dist/handlers/timers.d.ts.map +1 -1
  24. package/dist/handlers/utils.d.ts +12 -1
  25. package/dist/handlers/utils.d.ts.map +1 -1
  26. package/dist/handlers.js +1 -1
  27. package/dist/http.d.ts +1 -0
  28. package/dist/http.d.ts.map +1 -1
  29. package/dist/http.js +4 -3
  30. package/dist/http.js.map +1 -1
  31. package/dist/{index-B2DTeltj.js → index-D743zTS1.js} +196 -44
  32. package/dist/index-D743zTS1.js.map +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +3 -2
  35. package/dist/index.js.map +1 -1
  36. package/dist/instructions.d.ts +11 -0
  37. package/dist/instructions.d.ts.map +1 -0
  38. package/dist/schema.d.ts +218 -0
  39. package/dist/schema.d.ts.map +1 -0
  40. package/dist/server.js +1 -1
  41. package/dist/stdio.js +1 -1
  42. package/dist/tools.d.ts +6 -0
  43. package/dist/tools.d.ts.map +1 -1
  44. package/dist/tools.js +21 -0
  45. package/dist/tools.js.map +1 -1
  46. package/dist/version-i2-GF6d1.js +21 -0
  47. package/dist/version-i2-GF6d1.js.map +1 -0
  48. package/package.json +13 -3
  49. package/skills/SKILL.md +330 -0
  50. package/dist/index-B2DTeltj.js.map +0 -1
  51. package/dist/version-B1rjMbl8.js +0 -5
  52. package/dist/version-B1rjMbl8.js.map +0 -1
@@ -1,4 +1,100 @@
1
1
  import { formatBooking as formatBooking$1, formatListResponse as formatListResponse$1, formatDeal as formatDeal$1, formatTimer as formatTimer$1, formatComment as formatComment$1, formatCompany as formatCompany$1, formatPerson as formatPerson$1, formatService as formatService$1, formatTask as formatTask$1, formatTimeEntry as formatTimeEntry$1, formatProject as formatProject$1, ProductiveApi } from "@studiometa/productive-cli";
2
+ class UserInputError extends Error {
3
+ hints;
4
+ constructor(message, hints) {
5
+ super(message);
6
+ this.name = "UserInputError";
7
+ this.hints = hints;
8
+ }
9
+ /**
10
+ * Format error message with hints for LLM consumption
11
+ */
12
+ toFormattedMessage() {
13
+ let msg = `**Input Error:** ${this.message}`;
14
+ if (this.hints && this.hints.length > 0) {
15
+ msg += "\n\n**Hints:**\n" + this.hints.map((h) => `- ${h}`).join("\n");
16
+ }
17
+ return msg;
18
+ }
19
+ }
20
+ const ErrorMessages = {
21
+ // Required field errors
22
+ missingId: (action) => new UserInputError(`id is required for ${action} action`, [
23
+ `Use action="list" first to find the resource ID`,
24
+ `Then use action="${action}" with the id parameter`
25
+ ]),
26
+ missingRequiredFields: (resource, fields) => new UserInputError(
27
+ `${fields.join(", ")} ${fields.length === 1 ? "is" : "are"} required for creating ${resource}`,
28
+ [
29
+ `Provide all required fields: ${fields.join(", ")}`,
30
+ `Use action="help" for detailed documentation on ${resource}`
31
+ ]
32
+ ),
33
+ // Invalid action errors
34
+ invalidAction: (action, resource, validActions) => new UserInputError(`Invalid action "${action}" for ${resource}`, [
35
+ `Valid actions are: ${validActions.join(", ")}`,
36
+ `Use action="help" with resource="${resource}" for detailed documentation`
37
+ ]),
38
+ // Unknown resource errors
39
+ unknownResource: (resource, validResources) => new UserInputError(`Unknown resource: ${resource}`, [
40
+ `Valid resources are: ${validResources.join(", ")}`,
41
+ `Use action="help" without a resource for an overview of all resources`
42
+ ]),
43
+ // Report-specific errors
44
+ missingReportType: () => new UserInputError("report_type is required for reports", [
45
+ 'Specify report_type parameter (e.g., "time_reports", "project_reports")',
46
+ 'Use action="help" with resource="reports" for available report types'
47
+ ]),
48
+ invalidReportType: (reportType, validTypes) => new UserInputError(`Invalid report_type: ${reportType}`, [
49
+ `Valid report types are: ${validTypes.join(", ")}`,
50
+ 'Use action="help" with resource="reports" for detailed documentation'
51
+ ]),
52
+ // Timer-specific errors
53
+ missingServiceForTimer: () => new UserInputError("service_id is required to start a timer", [
54
+ 'First find a service using resource="services" action="list"',
55
+ "Then start the timer with the service_id"
56
+ ]),
57
+ // People-specific errors
58
+ noUserIdConfigured: () => new UserInputError("User ID not configured", [
59
+ 'The "me" action requires a user ID to be configured',
60
+ 'Use action="list" to find people, or configure the user ID'
61
+ ]),
62
+ // Comment-specific errors
63
+ missingCommentTarget: () => new UserInputError("A target is required for creating a comment", [
64
+ "Provide one of: task_id, deal_id, or company_id",
65
+ 'Find targets using resource="tasks", "deals", or "companies" with action="list"'
66
+ ]),
67
+ // Booking-specific errors
68
+ missingBookingTarget: () => new UserInputError("A service or event is required for creating a booking", [
69
+ "Provide either: service_id or event_id",
70
+ 'Find services using resource="services" with action="list"'
71
+ ]),
72
+ // API errors
73
+ apiError: (statusCode, message) => {
74
+ const hints = [];
75
+ if (statusCode === 401) {
76
+ hints.push("Check that your API token is valid and not expired");
77
+ hints.push("Verify the organization ID is correct");
78
+ } else if (statusCode === 403) {
79
+ hints.push("You may not have permission to access this resource");
80
+ hints.push("Check your API token permissions");
81
+ } else if (statusCode === 404) {
82
+ hints.push("The resource may not exist or you may not have access");
83
+ hints.push("Verify the resource ID is correct");
84
+ hints.push('Use action="list" to find valid resource IDs');
85
+ } else if (statusCode === 422) {
86
+ hints.push("The request data may be invalid");
87
+ hints.push("Check the field values and types");
88
+ hints.push('Use action="help" for field documentation');
89
+ } else if (statusCode >= 500) {
90
+ hints.push("This is a server error - try again later");
91
+ }
92
+ return new UserInputError(`API error (${statusCode}): ${message}`, hints);
93
+ }
94
+ };
95
+ function isUserInputError(error) {
96
+ return error instanceof UserInputError;
97
+ }
2
98
  const MCP_FORMAT_OPTIONS = {
3
99
  includeRelationshipIds: false,
4
100
  includeTimestamps: false,
@@ -101,10 +197,23 @@ function jsonResult(data) {
101
197
  }
102
198
  function errorResult(message) {
103
199
  return {
104
- content: [{ type: "text", text: `Error: ${message}` }],
200
+ content: [{ type: "text", text: `**Error:** ${message}` }],
105
201
  isError: true
106
202
  };
107
203
  }
204
+ function inputErrorResult(error) {
205
+ return {
206
+ content: [{ type: "text", text: error.toFormattedMessage() }],
207
+ isError: true
208
+ };
209
+ }
210
+ function formatError(error) {
211
+ if (isUserInputError(error)) {
212
+ return inputErrorResult(error);
213
+ }
214
+ const message = error instanceof Error ? error.message : String(error);
215
+ return errorResult(message);
216
+ }
108
217
  function toStringFilter(filter) {
109
218
  if (!filter) return void 0;
110
219
  const result = {};
@@ -116,21 +225,24 @@ function toStringFilter(filter) {
116
225
  return Object.keys(result).length > 0 ? result : void 0;
117
226
  }
118
227
  const DEFAULT_BOOKING_INCLUDE = ["person", "service"];
228
+ const VALID_ACTIONS$a = ["list", "get", "create", "update"];
119
229
  async function handleBookings(action, args, ctx) {
120
230
  const { api, formatOptions, filter, page, perPage, include: userInclude } = ctx;
121
231
  const { id, person_id, service_id, event_id, started_on, ended_on, time, note } = args;
122
232
  const include = userInclude?.length ? [.../* @__PURE__ */ new Set([...DEFAULT_BOOKING_INCLUDE, ...userInclude])] : DEFAULT_BOOKING_INCLUDE;
123
233
  if (action === "get") {
124
- if (!id) return errorResult("id is required for get action");
234
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
125
235
  const result = await api.getBooking(id, { include });
126
236
  return jsonResult(formatBooking(result.data, { ...formatOptions, included: result.included }));
127
237
  }
128
238
  if (action === "create") {
129
239
  if (!person_id || !started_on || !ended_on) {
130
- return errorResult("person_id, started_on, and ended_on are required for create");
240
+ return inputErrorResult(
241
+ ErrorMessages.missingRequiredFields("booking", ["person_id", "started_on", "ended_on"])
242
+ );
131
243
  }
132
244
  if (!service_id && !event_id) {
133
- return errorResult("service_id or event_id is required for create");
245
+ return inputErrorResult(ErrorMessages.missingBookingTarget());
134
246
  }
135
247
  const result = await api.createBooking({
136
248
  person_id,
@@ -144,7 +256,7 @@ async function handleBookings(action, args, ctx) {
144
256
  return jsonResult({ success: true, ...formatBooking(result.data, formatOptions) });
145
257
  }
146
258
  if (action === "update") {
147
- if (!id) return errorResult("id is required for update action");
259
+ if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
148
260
  const updateData = {};
149
261
  if (started_on !== void 0) updateData.started_on = started_on;
150
262
  if (ended_on !== void 0) updateData.ended_on = ended_on;
@@ -162,22 +274,23 @@ async function handleBookings(action, args, ctx) {
162
274
  })
163
275
  );
164
276
  }
165
- return errorResult(`Invalid action "${action}" for bookings. Use: list, get, create, update`);
277
+ return inputErrorResult(ErrorMessages.invalidAction(action, "bookings", VALID_ACTIONS$a));
166
278
  }
167
279
  const DEFAULT_COMMENT_INCLUDE = ["creator"];
280
+ const VALID_ACTIONS$9 = ["list", "get", "create", "update"];
168
281
  async function handleComments(action, args, ctx) {
169
282
  const { api, formatOptions, filter, page, perPage, include: userInclude } = ctx;
170
283
  const { id, body, task_id, deal_id, company_id } = args;
171
284
  const include = userInclude?.length ? [.../* @__PURE__ */ new Set([...DEFAULT_COMMENT_INCLUDE, ...userInclude])] : DEFAULT_COMMENT_INCLUDE;
172
285
  if (action === "get") {
173
- if (!id) return errorResult("id is required for get action");
286
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
174
287
  const result = await api.getComment(id, { include });
175
288
  return jsonResult(formatComment(result.data, { ...formatOptions, included: result.included }));
176
289
  }
177
290
  if (action === "create") {
178
- if (!body) return errorResult("body is required for create");
291
+ if (!body) return inputErrorResult(ErrorMessages.missingRequiredFields("comment", ["body"]));
179
292
  if (!task_id && !deal_id && !company_id) {
180
- return errorResult("task_id, deal_id, or company_id is required for create");
293
+ return inputErrorResult(ErrorMessages.missingCommentTarget());
181
294
  }
182
295
  const result = await api.createComment({
183
296
  body,
@@ -188,8 +301,9 @@ async function handleComments(action, args, ctx) {
188
301
  return jsonResult({ success: true, ...formatComment(result.data, formatOptions) });
189
302
  }
190
303
  if (action === "update") {
191
- if (!id) return errorResult("id is required for update action");
192
- if (!body) return errorResult("body is required for update");
304
+ if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
305
+ if (!body)
306
+ return inputErrorResult(ErrorMessages.missingRequiredFields("comment update", ["body"]));
193
307
  const result = await api.updateComment(id, { body });
194
308
  return jsonResult({ success: true, ...formatComment(result.data, formatOptions) });
195
309
  }
@@ -202,23 +316,24 @@ async function handleComments(action, args, ctx) {
202
316
  })
203
317
  );
204
318
  }
205
- return errorResult(`Invalid action "${action}" for comments. Use: list, get, create, update`);
319
+ return inputErrorResult(ErrorMessages.invalidAction(action, "comments", VALID_ACTIONS$9));
206
320
  }
321
+ const VALID_ACTIONS$8 = ["list", "get", "create", "update"];
207
322
  async function handleCompanies(action, args, ctx) {
208
323
  const { api, formatOptions, filter, page, perPage } = ctx;
209
324
  const { id, name } = args;
210
325
  if (action === "get") {
211
- if (!id) return errorResult("id is required for get action");
326
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
212
327
  const result = await api.getCompany(id);
213
328
  return jsonResult(formatCompany(result.data, formatOptions));
214
329
  }
215
330
  if (action === "create") {
216
- if (!name) return errorResult("name is required for create");
331
+ if (!name) return inputErrorResult(ErrorMessages.missingRequiredFields("company", ["name"]));
217
332
  const result = await api.createCompany({ name });
218
333
  return jsonResult({ success: true, ...formatCompany(result.data, formatOptions) });
219
334
  }
220
335
  if (action === "update") {
221
- if (!id) return errorResult("id is required for update action");
336
+ if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
222
337
  const updateData = {};
223
338
  if (name !== void 0) updateData.name = name;
224
339
  const result = await api.updateCompany(id, updateData);
@@ -228,28 +343,29 @@ async function handleCompanies(action, args, ctx) {
228
343
  const result = await api.getCompanies({ filter, page, perPage });
229
344
  return jsonResult(formatListResponse(result.data, formatCompany, result.meta, formatOptions));
230
345
  }
231
- return errorResult(`Invalid action "${action}" for companies. Use: list, get, create, update`);
346
+ return inputErrorResult(ErrorMessages.invalidAction(action, "companies", VALID_ACTIONS$8));
232
347
  }
233
348
  const DEFAULT_DEAL_INCLUDE_GET = ["company", "deal_status", "responsible"];
234
349
  const DEFAULT_DEAL_INCLUDE_LIST = ["company", "deal_status"];
350
+ const VALID_ACTIONS$7 = ["list", "get", "create", "update"];
235
351
  async function handleDeals(action, args, ctx) {
236
352
  const { api, formatOptions, filter, page, perPage, include: userInclude } = ctx;
237
353
  const { id, name, company_id } = args;
238
354
  if (action === "get") {
239
- if (!id) return errorResult("id is required for get action");
355
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
240
356
  const include = userInclude?.length ? [.../* @__PURE__ */ new Set([...DEFAULT_DEAL_INCLUDE_GET, ...userInclude])] : DEFAULT_DEAL_INCLUDE_GET;
241
357
  const result = await api.getDeal(id, { include });
242
358
  return jsonResult(formatDeal(result.data, { ...formatOptions, included: result.included }));
243
359
  }
244
360
  if (action === "create") {
245
361
  if (!name || !company_id) {
246
- return errorResult("name and company_id are required for create");
362
+ return inputErrorResult(ErrorMessages.missingRequiredFields("deal", ["name", "company_id"]));
247
363
  }
248
364
  const result = await api.createDeal({ name, company_id });
249
365
  return jsonResult({ success: true, ...formatDeal(result.data, formatOptions) });
250
366
  }
251
367
  if (action === "update") {
252
- if (!id) return errorResult("id is required for update action");
368
+ if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
253
369
  const updateData = {};
254
370
  if (name !== void 0) updateData.name = name;
255
371
  const result = await api.updateDeal(id, updateData);
@@ -270,7 +386,7 @@ async function handleDeals(action, args, ctx) {
270
386
  })
271
387
  );
272
388
  }
273
- return errorResult(`Invalid action "${action}" for deals. Use: list, get, create, update`);
389
+ return inputErrorResult(ErrorMessages.invalidAction(action, "deals", VALID_ACTIONS$7));
274
390
  }
275
391
  const RESOURCE_HELP = {
276
392
  projects: {
@@ -685,11 +801,12 @@ function handleHelpOverview() {
685
801
  resources: overview
686
802
  });
687
803
  }
804
+ const VALID_ACTIONS$6 = ["list", "get", "me"];
688
805
  async function handlePeople(action, args, ctx, credentials) {
689
806
  const { api, formatOptions, filter, page, perPage } = ctx;
690
807
  const { id } = args;
691
808
  if (action === "get") {
692
- if (!id) return errorResult("id is required for get action");
809
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
693
810
  const result = await api.getPerson(id);
694
811
  return jsonResult(formatPerson(result.data, formatOptions));
695
812
  }
@@ -700,6 +817,7 @@ async function handlePeople(action, args, ctx, credentials) {
700
817
  }
701
818
  return jsonResult({
702
819
  message: "User ID not configured. Set userId in credentials to use this action.",
820
+ hint: 'Use action="list" to find people, or configure the user ID in your credentials.',
703
821
  organizationId: credentials.organizationId
704
822
  });
705
823
  }
@@ -707,13 +825,14 @@ async function handlePeople(action, args, ctx, credentials) {
707
825
  const result = await api.getPeople({ filter, page, perPage });
708
826
  return jsonResult(formatListResponse(result.data, formatPerson, result.meta, formatOptions));
709
827
  }
710
- return errorResult(`Invalid action "${action}" for people. Use: list, get, me`);
828
+ return inputErrorResult(ErrorMessages.invalidAction(action, "people", VALID_ACTIONS$6));
711
829
  }
830
+ const VALID_ACTIONS$5 = ["list", "get"];
712
831
  async function handleProjects(action, args, ctx) {
713
832
  const { api, formatOptions, filter, page, perPage } = ctx;
714
833
  const { id } = args;
715
834
  if (action === "get") {
716
- if (!id) return errorResult("id is required for get action");
835
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
717
836
  const result = await api.getProject(id);
718
837
  return jsonResult(formatProject(result.data, formatOptions));
719
838
  }
@@ -721,7 +840,7 @@ async function handleProjects(action, args, ctx) {
721
840
  const result = await api.getProjects({ filter, page, perPage });
722
841
  return jsonResult(formatListResponse(result.data, formatProject, result.meta, formatOptions));
723
842
  }
724
- return errorResult(`Invalid action "${action}" for projects. Use: list, get`);
843
+ return inputErrorResult(ErrorMessages.invalidAction(action, "projects", VALID_ACTIONS$5));
725
844
  }
726
845
  function formatReportData(data) {
727
846
  return data.map((item) => {
@@ -746,19 +865,18 @@ const VALID_REPORT_TYPES = [
746
865
  "deal_reports",
747
866
  "timesheet_reports"
748
867
  ];
868
+ const VALID_ACTIONS$4 = ["get"];
749
869
  async function handleReports(action, args, ctx) {
750
870
  const { api, filter, page, perPage } = ctx;
751
871
  const { report_type, group, from, to, person_id, project_id, company_id, deal_id, status } = args;
752
872
  if (action !== "get") {
753
- return errorResult(`Invalid action "${action}" for reports. Use: get`);
873
+ return inputErrorResult(ErrorMessages.invalidAction(action, "reports", VALID_ACTIONS$4));
754
874
  }
755
875
  if (!report_type) {
756
- return errorResult(`report_type is required. Valid types: ${VALID_REPORT_TYPES.join(", ")}`);
876
+ return inputErrorResult(ErrorMessages.missingReportType());
757
877
  }
758
878
  if (!VALID_REPORT_TYPES.includes(report_type)) {
759
- return errorResult(
760
- `Invalid report_type "${report_type}". Valid types: ${VALID_REPORT_TYPES.join(", ")}`
761
- );
879
+ return inputErrorResult(ErrorMessages.invalidReportType(report_type, VALID_REPORT_TYPES));
762
880
  }
763
881
  const reportFilter = { ...filter };
764
882
  if (from) {
@@ -844,27 +962,31 @@ async function handleReports(action, args, ctx) {
844
962
  meta: result.meta
845
963
  });
846
964
  }
965
+ const VALID_ACTIONS$3 = ["list"];
847
966
  async function handleServices(action, _args, ctx) {
848
967
  const { api, formatOptions, filter, page, perPage } = ctx;
849
968
  if (action === "list") {
850
969
  const result = await api.getServices({ filter, page, perPage });
851
970
  return jsonResult(formatListResponse(result.data, formatService, result.meta, formatOptions));
852
971
  }
853
- return errorResult(`Invalid action "${action}" for services. Use: list`);
972
+ return inputErrorResult(ErrorMessages.invalidAction(action, "services", VALID_ACTIONS$3));
854
973
  }
855
974
  const DEFAULT_TASK_INCLUDE = ["project", "project.company"];
975
+ const VALID_ACTIONS$2 = ["list", "get", "create", "update"];
856
976
  async function handleTasks(action, args, ctx) {
857
977
  const { api, formatOptions, filter, page, perPage, include: userInclude } = ctx;
858
978
  const { id, title, project_id, task_list_id, description, assignee_id } = args;
859
979
  const include = userInclude?.length ? [.../* @__PURE__ */ new Set([...DEFAULT_TASK_INCLUDE, ...userInclude])] : DEFAULT_TASK_INCLUDE;
860
980
  if (action === "get") {
861
- if (!id) return errorResult("id is required for get action");
981
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
862
982
  const result = await api.getTask(id, { include });
863
983
  return jsonResult(formatTask(result.data, { ...formatOptions, included: result.included }));
864
984
  }
865
985
  if (action === "create") {
866
986
  if (!title || !project_id || !task_list_id) {
867
- return errorResult("title, project_id, and task_list_id are required for create");
987
+ return inputErrorResult(
988
+ ErrorMessages.missingRequiredFields("task", ["title", "project_id", "task_list_id"])
989
+ );
868
990
  }
869
991
  const result = await api.createTask({
870
992
  title,
@@ -876,7 +998,7 @@ async function handleTasks(action, args, ctx) {
876
998
  return jsonResult({ success: true, ...formatTask(result.data, formatOptions) });
877
999
  }
878
1000
  if (action === "update") {
879
- if (!id) return errorResult("id is required for update action");
1001
+ if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
880
1002
  const updateData = {};
881
1003
  if (title !== void 0) updateData.title = title;
882
1004
  if (description !== void 0) updateData.description = description;
@@ -893,19 +1015,27 @@ async function handleTasks(action, args, ctx) {
893
1015
  })
894
1016
  );
895
1017
  }
896
- return errorResult(`Invalid action "${action}" for tasks. Use: list, get, create, update`);
1018
+ return inputErrorResult(ErrorMessages.invalidAction(action, "tasks", VALID_ACTIONS$2));
897
1019
  }
1020
+ const VALID_ACTIONS$1 = ["list", "get", "create", "update"];
898
1021
  async function handleTime(action, args, ctx) {
899
1022
  const { api, formatOptions, filter, page, perPage } = ctx;
900
1023
  const { id, person_id, service_id, task_id, time, date, note } = args;
901
1024
  if (action === "get") {
902
- if (!id) return errorResult("id is required for get action");
1025
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
903
1026
  const result = await api.getTimeEntry(id);
904
1027
  return jsonResult(formatTimeEntry(result.data, formatOptions));
905
1028
  }
906
1029
  if (action === "create") {
907
1030
  if (!person_id || !service_id || !time || !date) {
908
- return errorResult("person_id, service_id, time, and date are required for create");
1031
+ return inputErrorResult(
1032
+ ErrorMessages.missingRequiredFields("time entry", [
1033
+ "person_id",
1034
+ "service_id",
1035
+ "time",
1036
+ "date"
1037
+ ])
1038
+ );
909
1039
  }
910
1040
  const result = await api.createTimeEntry({
911
1041
  person_id,
@@ -918,7 +1048,7 @@ async function handleTime(action, args, ctx) {
918
1048
  return jsonResult({ success: true, ...formatTimeEntry(result.data, formatOptions) });
919
1049
  }
920
1050
  if (action === "update") {
921
- if (!id) return errorResult("id is required for update action");
1051
+ if (!id) return inputErrorResult(ErrorMessages.missingId("update"));
922
1052
  const updateData = {};
923
1053
  if (time !== void 0) updateData.time = time;
924
1054
  if (date !== void 0) updateData.date = date;
@@ -930,25 +1060,26 @@ async function handleTime(action, args, ctx) {
930
1060
  const result = await api.getTimeEntries({ filter, page, perPage });
931
1061
  return jsonResult(formatListResponse(result.data, formatTimeEntry, result.meta, formatOptions));
932
1062
  }
933
- return errorResult(`Invalid action "${action}" for time. Use: list, get, create, update`);
1063
+ return inputErrorResult(ErrorMessages.invalidAction(action, "time", VALID_ACTIONS$1));
934
1064
  }
1065
+ const VALID_ACTIONS = ["list", "get", "start", "stop"];
935
1066
  async function handleTimers(action, args, ctx) {
936
1067
  const { api, formatOptions, filter, page, perPage, include } = ctx;
937
1068
  const { id, service_id, time_entry_id } = args;
938
1069
  if (action === "get") {
939
- if (!id) return errorResult("id is required for get action");
1070
+ if (!id) return inputErrorResult(ErrorMessages.missingId("get"));
940
1071
  const result = await api.getTimer(id, { include });
941
1072
  return jsonResult(formatTimer(result.data));
942
1073
  }
943
1074
  if (action === "start" || action === "create") {
944
1075
  if (!service_id && !time_entry_id) {
945
- return errorResult("service_id or time_entry_id is required to start a timer");
1076
+ return inputErrorResult(ErrorMessages.missingServiceForTimer());
946
1077
  }
947
1078
  const result = await api.startTimer({ service_id, time_entry_id });
948
1079
  return jsonResult({ success: true, ...formatTimer(result.data) });
949
1080
  }
950
1081
  if (action === "stop") {
951
- if (!id) return errorResult("id is required to stop a timer");
1082
+ if (!id) return inputErrorResult(ErrorMessages.missingId("stop"));
952
1083
  const result = await api.stopTimer(id);
953
1084
  return jsonResult({ success: true, ...formatTimer(result.data) });
954
1085
  }
@@ -956,8 +1087,21 @@ async function handleTimers(action, args, ctx) {
956
1087
  const result = await api.getTimers({ filter, page, perPage, include });
957
1088
  return jsonResult(formatListResponse(result.data, formatTimer, result.meta, formatOptions));
958
1089
  }
959
- return errorResult(`Invalid action "${action}" for timers. Use: list, get, start, stop`);
1090
+ return inputErrorResult(ErrorMessages.invalidAction(action, "timers", VALID_ACTIONS));
960
1091
  }
1092
+ const VALID_RESOURCES = [
1093
+ "projects",
1094
+ "time",
1095
+ "tasks",
1096
+ "services",
1097
+ "people",
1098
+ "companies",
1099
+ "comments",
1100
+ "timers",
1101
+ "deals",
1102
+ "bookings",
1103
+ "reports"
1104
+ ];
961
1105
  const DEFAULT_PER_PAGE = 20;
962
1106
  async function executeToolWithCredentials(name, args, credentials) {
963
1107
  const api = new ProductiveApi({
@@ -1012,14 +1156,22 @@ async function executeToolWithCredentials(name, args, credentials) {
1012
1156
  case "reports":
1013
1157
  return await handleReports(action, restArgs, ctx);
1014
1158
  default:
1015
- return errorResult(`Unknown resource: ${resource}`);
1159
+ return inputErrorResult(ErrorMessages.unknownResource(resource, VALID_RESOURCES));
1016
1160
  }
1017
1161
  } catch (error) {
1162
+ if (isUserInputError(error)) {
1163
+ return formatError(error);
1164
+ }
1018
1165
  const message = error instanceof Error ? error.message : String(error);
1166
+ const statusMatch = message.match(/(\d{3})/);
1167
+ if (statusMatch) {
1168
+ const statusCode = Number.parseInt(statusMatch[1], 10);
1169
+ return inputErrorResult(ErrorMessages.apiError(statusCode, message));
1170
+ }
1019
1171
  return errorResult(message);
1020
1172
  }
1021
1173
  }
1022
1174
  export {
1023
1175
  executeToolWithCredentials as e
1024
1176
  };
1025
- //# sourceMappingURL=index-B2DTeltj.js.map
1177
+ //# sourceMappingURL=index-D743zTS1.js.map