@jorgeluismlima/teamwork-mcp 1.0.2 → 1.1.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 (3) hide show
  1. package/README.md +11 -1
  2. package/dist/index.js +479 -2
  3. package/package.json +4 -3
package/README.md CHANGED
@@ -21,11 +21,21 @@ This server exposes the following tools:
21
21
  * **`list_of_teamwork_project_categories`**: Helper tool to list categories.
22
22
 
23
23
  ### People & Companies
24
- * **`get_project_people`**: Lists all people associated with a project.
24
+ * **`list_people`**: Lists all people across the site with comprehensive filtering (user type, search term, job role, etc.).
25
+ * **`get_project_people`**: Lists all people associated with a project, with 30+ optional filters.
25
26
  * **`get_person`**: Retrieves detailed information about a specific person (user).
26
27
  * **`list_companies`**: Lists companies (clients).
27
28
  * **`get_company`**: Retrieves details of a specific company.
28
29
 
30
+ ### Projects & Updates
31
+ * **`get_project`**: Retrieves detailed information about a specific project, with comprehensive filtering and include options.
32
+ * **`get_all_project_updates`**: Lists updates across all projects.
33
+ * **`get_project_updates`**: Lists updates for a specific project.
34
+
35
+ ### Tags
36
+ * **`get_all_tags`**: Lists all tags with filtering by name, item type, etc.
37
+ * **`get_tag`**: Retrieves details of a specific tag.
38
+
29
39
  ## Configuration
30
40
 
31
41
  To use this server, you need to configure the following environment variables:
package/dist/index.js CHANGED
@@ -315,11 +315,134 @@ server.tool("get_time_entries", {
315
315
  return handleApiError(error);
316
316
  }
317
317
  });
318
+ server.tool("list_people", {
319
+ userType: z.enum(["account", "collaborator", "contact"]).optional().describe("user type"),
320
+ updatedAfter: z.string().optional().describe("date time"),
321
+ searchTerm: z.string().optional().describe("filter by comment content"),
322
+ orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
323
+ orderBy: z.enum(["name", "namecaseinsensitive", "company"]).optional().describe("order by"),
324
+ lastLoginAfter: z.string().optional().describe("last login after"),
325
+ pageSize: z.number().optional().describe("number of items in a page"),
326
+ page: z.number().optional().describe("page number"),
327
+ skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts"),
328
+ showDeleted: z.boolean().optional().describe("include deleted items"),
329
+ searchUserJobRole: z.boolean().optional().describe("Include user job role in search"),
330
+ orderPrioritiseCurrentUser: z.boolean().optional().describe("Force to have the current/session user in the response"),
331
+ onlySiteOwner: z.boolean().optional().describe("only site owner"),
332
+ onlyOwnerCompany: z.boolean().optional().describe("return people only from the owner company"),
333
+ inclusiveFilter: z.boolean().optional().describe("make the filter inclusive for user ids, teamIds, companyIds"),
334
+ includeServiceAccounts: z.boolean().optional().describe("include service accounts"),
335
+ includePlaceholders: z.boolean().optional().describe("include placeholder users"),
336
+ includeCollaborators: z.boolean().optional().describe("exclude collaborators types"),
337
+ includeClients: z.boolean().optional().describe("include clients"),
338
+ filterByNoCostRate: z.boolean().optional().describe("Returns users who are missing cost rates"),
339
+ excludeContacts: z.boolean().optional().describe("exclude contact types"),
340
+ adminsOnly: z.boolean().optional().describe("only include users with administrator status"),
341
+ teamIds: z.array(z.number()).optional().describe("team ids"),
342
+ projectIds: z.array(z.number()).optional().describe("filter by project ids"),
343
+ jobRoleIds: z.array(z.number()).optional().describe("filter by job role ids"),
344
+ include: z.array(z.string()).optional().describe("include related data"),
345
+ ids: z.array(z.number()).optional().describe("filter by user ids"),
346
+ excludeProjectIds: z.array(z.number()).optional().describe("exclude people assigned to certain project id"),
347
+ excludeIds: z.array(z.number()).optional().describe("exclude certain user ids"),
348
+ emails: z.array(z.string()).optional().describe("filter by user emails"),
349
+ companyIds: z.array(z.number()).optional().describe("company ids"),
350
+ }, async (args) => {
351
+ try {
352
+ const params = { ...args };
353
+ // Helper to join arrays
354
+ const joinParam = (arr) => arr ? arr.join(',') : undefined;
355
+ if (args.teamIds)
356
+ params.teamIds = joinParam(args.teamIds);
357
+ if (args.projectIds)
358
+ params.projectIds = joinParam(args.projectIds);
359
+ if (args.jobRoleIds)
360
+ params.jobRoleIds = joinParam(args.jobRoleIds);
361
+ if (args.include)
362
+ params.include = joinParam(args.include);
363
+ if (args.ids)
364
+ params.ids = joinParam(args.ids);
365
+ if (args.excludeProjectIds)
366
+ params.excludeProjectIds = joinParam(args.excludeProjectIds);
367
+ if (args.excludeIds)
368
+ params.excludeIds = joinParam(args.excludeIds);
369
+ if (args.emails)
370
+ params.emails = joinParam(args.emails);
371
+ if (args.companyIds)
372
+ params.companyIds = joinParam(args.companyIds);
373
+ const response = await axiosInstance.get("/people.json", { params });
374
+ return {
375
+ content: [
376
+ {
377
+ type: "text",
378
+ text: JSON.stringify(response.data, null, 2),
379
+ },
380
+ ],
381
+ };
382
+ }
383
+ catch (error) {
384
+ return handleApiError(error);
385
+ }
386
+ });
318
387
  server.tool("get_project_people", {
319
388
  projectId: z.number().describe("The ID of the project"),
320
- }, async ({ projectId }) => {
389
+ userType: z.enum(["account", "collaborator", "contact"]).optional().describe("user type"),
390
+ updatedAfter: z.string().optional().describe("date time"),
391
+ searchTerm: z.string().optional().describe("filter by comment content"),
392
+ orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
393
+ orderBy: z.enum(["name", "namecaseinsensitive", "company"]).optional().describe("order by"),
394
+ lastLoginAfter: z.string().optional().describe("last login after"),
395
+ pageSize: z.number().optional().describe("number of items in a page"),
396
+ page: z.number().optional().describe("page number"),
397
+ skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts"),
398
+ showDeleted: z.boolean().optional().describe("include deleted items"),
399
+ searchUserJobRole: z.boolean().optional().describe("Include user job role in search"),
400
+ orderPrioritiseCurrentUser: z.boolean().optional().describe("Force to have the current/session user in the response"),
401
+ onlySiteOwner: z.boolean().optional().describe("only site owner"),
402
+ onlyOwnerCompany: z.boolean().optional().describe("return people only from the owner company"),
403
+ inclusiveFilter: z.boolean().optional().describe("make the filter inclusive"),
404
+ includeServiceAccounts: z.boolean().optional().describe("include service accounts"),
405
+ includePlaceholders: z.boolean().optional().describe("include placeholder users"),
406
+ includeObservers: z.boolean().optional().describe("include project observers"),
407
+ includeCollaborators: z.boolean().optional().describe("exclude collaborators types"),
408
+ includeClients: z.boolean().optional().describe("include clients"),
409
+ filterByNoCostRate: z.boolean().optional().describe("Returns users who are missing cost rates"),
410
+ excludeContacts: z.boolean().optional().describe("exclude contact types"),
411
+ adminsOnly: z.boolean().optional().describe("only include users with administrator status"),
412
+ teamIds: z.array(z.number()).optional().describe("team ids"),
413
+ projectIds: z.array(z.number()).optional().describe("filter by project ids"),
414
+ jobRoleIds: z.array(z.number()).optional().describe("filter by job role ids"),
415
+ include: z.array(z.string()).optional().describe("include related data"),
416
+ ids: z.array(z.number()).optional().describe("filter by user ids"),
417
+ excludeProjectIds: z.array(z.number()).optional().describe("exclude people assigned to certain project id"),
418
+ excludeIds: z.array(z.number()).optional().describe("exclude certain user ids"),
419
+ emails: z.array(z.string()).optional().describe("filter by user emails"),
420
+ companyIds: z.array(z.number()).optional().describe("company ids"),
421
+ }, async (args) => {
321
422
  try {
322
- const response = await axiosInstance.get(`/projects/${projectId}/people.json`);
423
+ const { projectId, ...restParams } = args;
424
+ const params = { ...restParams };
425
+ // Helper to join arrays
426
+ const joinParam = (arr) => arr ? arr.join(',') : undefined;
427
+ if (args.teamIds)
428
+ params.teamIds = joinParam(args.teamIds);
429
+ if (args.projectIds)
430
+ params.projectIds = joinParam(args.projectIds);
431
+ if (args.jobRoleIds)
432
+ params.jobRoleIds = joinParam(args.jobRoleIds);
433
+ if (args.include)
434
+ params.include = joinParam(args.include);
435
+ if (args.ids)
436
+ params.ids = joinParam(args.ids);
437
+ if (args.excludeProjectIds)
438
+ params.excludeProjectIds = joinParam(args.excludeProjectIds);
439
+ if (args.excludeIds)
440
+ params.excludeIds = joinParam(args.excludeIds);
441
+ if (args.emails)
442
+ params.emails = joinParam(args.emails);
443
+ if (args.companyIds)
444
+ params.companyIds = joinParam(args.companyIds);
445
+ const response = await axiosInstance.get(`/projects/${projectId}/people.json`, { params });
323
446
  return {
324
447
  content: [
325
448
  {
@@ -603,6 +726,360 @@ server.tool("list_of_teamwork_project_categories", {}, async () => {
603
726
  return handleApiError(error);
604
727
  }
605
728
  });
729
+ server.tool("get_project", {
730
+ projectId: z.number().describe("The ID of the project"),
731
+ updatedAfter: z.string().optional().describe("updated after"),
732
+ timeMode: z.enum(["timelogs", "estimated"]).optional().describe("profitability time mode"),
733
+ searchTerm: z.string().optional().describe("filter by project name"),
734
+ reportType: z.enum(["project", "health"]).optional().describe("define the type of the report"),
735
+ reportTimezone: z.string().optional().describe("Optional to configure the report dates displayed in a timezone"),
736
+ reportFormat: z.enum(["csv", "html", "pdf", "xls"]).optional().describe("define the format of the report"),
737
+ projectType: z.string().optional().describe("filter by project type"),
738
+ orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
739
+ orderBy: z.enum(["companyname", "datecreated", "duedate", "lastactivity", "name", "namecaseinsensitive", "ownercompany", "starred", "categoryname"]).optional().describe("order by"),
740
+ notCompletedBefore: z.string().optional().describe("filter by projects that have not been completed before the given date"),
741
+ minLastActivityDate: z.string().optional().describe("filter by min last activity date"),
742
+ maxLastActivityDate: z.string().optional().describe("filter by max last activity date"),
743
+ userId: z.number().optional().describe("filter by user id"),
744
+ pageSize: z.number().optional().describe("number of items in a page"),
745
+ page: z.number().optional().describe("page number"),
746
+ orderByCustomFieldId: z.number().optional().describe("order by custom field id when orderBy is equal to customfield"),
747
+ minBudgetCapacityUsedPercent: z.number().optional().describe("filter by minimum budget capacity used"),
748
+ maxBudgetCapacityUsedPercent: z.number().optional().describe("filter by maximum budget capacity used"),
749
+ useFormulaFields: z.boolean().optional().describe("use formula fields"),
750
+ skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts on a list API endpoint for performance reasons"),
751
+ searchCompanies: z.boolean().optional().describe("include companies in the search"),
752
+ searchByLetter: z.boolean().optional().describe("search projects beginning with the search term character only"),
753
+ onlyStarredProjects: z.boolean().optional().describe("filter by starred projects only"),
754
+ onlyProjectsWithExplicitMembership: z.boolean().optional().describe("only show projects with explicit membership"),
755
+ onlyProjectsThatCanLogTime: z.boolean().optional().describe("can log time on projects"),
756
+ onlyProjectsThatCanAddTasks: z.boolean().optional().describe("can add tasks on projects"),
757
+ onlyArchivedProjects: z.boolean().optional().describe("return only archived projects"),
758
+ matchAllProjectTags: z.boolean().optional().describe("match all project tags"),
759
+ matchAllExcludedTags: z.boolean().optional().describe("match all excluded project tags"),
760
+ isReportDownload: z.boolean().optional().describe("generate a report document"),
761
+ includeTentativeProjects: z.boolean().optional().describe("include alongside normal projects, tentative ones"),
762
+ includeTabSystemStatus: z.boolean().optional().describe("include tab system status in response"),
763
+ includeSubCategories: z.boolean().optional().describe("include sub categories when filtering by ids"),
764
+ includeStats: z.boolean().optional().describe("include project status counts"),
765
+ includeProjectUserInfo: z.boolean().optional().describe("fetch user-specific data such as isStarred"),
766
+ includeProjectProfitability: z.boolean().optional().describe("include project profitability in response"),
767
+ includeProjectDates: z.boolean().optional().describe("include minimum and maximum start/end dates for projects"),
768
+ includeCustomFields: z.boolean().optional().describe("include custom fields"),
769
+ includeCounts: z.boolean().optional().describe("include project related counts"),
770
+ includeCompletedStatus: z.boolean().optional().describe("include completed projects when filtering by project statuses current,late"),
771
+ includeArchivedProjects: z.boolean().optional().describe("include archived projects"),
772
+ hideObservedProjects: z.boolean().optional().describe("hide projects where the logged-in user is just an observer"),
773
+ alwaysIncludeFiltering: z.boolean().optional().describe("includes filters when project ids are provided"),
774
+ usersWithExplicitMembershipIds: z.array(z.number()).optional().describe("only show projects that have an explicit common membership with provided user ids"),
775
+ teamIds: z.array(z.number()).optional().describe("filter by projects that contain users associated with the team ids"),
776
+ selectedColumns: z.array(z.string()).optional().describe("select the columns to use in exports"),
777
+ projectTagIds: z.array(z.number()).optional().describe("filter by project tag ids"),
778
+ projectStatuses: z.array(z.string()).optional().describe("filter by project status (active, current, late, upcoming, completed, deleted)"),
779
+ projectOwnerIds: z.array(z.number()).optional().describe("filter by project owner ids"),
780
+ projectIds: z.array(z.number()).optional().describe("filter by project ids"),
781
+ projectHealths: z.array(z.number()).optional().describe("filter by project healths (0: not set, 1: bad, 2: ok, 3: good)"),
782
+ projectCompanyIds: z.array(z.number()).optional().describe("filter by company ids"),
783
+ projectCategoryIds: z.array(z.number()).optional().describe("filter by project category ids"),
784
+ includeCustomFieldIds: z.array(z.number()).optional().describe("include specific custom fields"),
785
+ include: z.array(z.string()).optional().describe("include related data"),
786
+ featuresEnabled: z.array(z.string()).optional().describe("filter by projects that have features enabled"),
787
+ excludeTagIds: z.array(z.number()).optional().describe("exclude by project tag ids"),
788
+ excludeProjectIds: z.array(z.number()).optional().describe("exclude certain project ids"),
789
+ // Field arrays for flattening
790
+ fieldsWorkflows: z.array(z.string()).optional().describe("fields[workflows]"),
791
+ fieldsUsers: z.array(z.string()).optional().describe("fields[users]"),
792
+ fieldsTags: z.array(z.string()).optional().describe("fields[tags]"),
793
+ fieldsStages: z.array(z.string()).optional().describe("fields[stages]"),
794
+ fieldsProjects: z.array(z.string()).optional().describe("fields[projects]"),
795
+ fieldsProjectCategories: z.array(z.string()).optional().describe("fields[projectcategories]"),
796
+ fieldsProjectUpdates: z.array(z.string()).optional().describe("fields[projectUpdates]"),
797
+ fieldsProjectBudgets: z.array(z.string()).optional().describe("fields[projectBudgets]"),
798
+ fieldsPortfolioColumns: z.array(z.string()).optional().describe("fields[portfolioColumns]"),
799
+ fieldsPortfolioCards: z.array(z.string()).optional().describe("fields[portfolioCards]"),
800
+ fieldsPortfolioBoards: z.array(z.string()).optional().describe("fields[portfolioBoards]"),
801
+ fieldsIndustries: z.array(z.string()).optional().describe("fields[industries]"),
802
+ fieldsCustomFields: z.array(z.string()).optional().describe("fields[customfields]"),
803
+ fieldsCustomFieldProjects: z.array(z.string()).optional().describe("fields[customfieldProjects]"),
804
+ fieldsCountries: z.array(z.string()).optional().describe("fields[countries]"),
805
+ fieldsCompanies: z.array(z.string()).optional().describe("fields[companies]"),
806
+ }, async (args) => {
807
+ try {
808
+ const { projectId, ...restParams } = args;
809
+ const params = { ...restParams };
810
+ const joinParam = (arr) => arr ? arr.join(',') : undefined;
811
+ if (args.usersWithExplicitMembershipIds)
812
+ params.usersWithExplicitMembershipIds = joinParam(args.usersWithExplicitMembershipIds);
813
+ if (args.teamIds)
814
+ params.teamIds = joinParam(args.teamIds);
815
+ if (args.selectedColumns)
816
+ params.selectedColumns = joinParam(args.selectedColumns);
817
+ if (args.projectTagIds)
818
+ params.projectTagIds = joinParam(args.projectTagIds);
819
+ if (args.projectStatuses)
820
+ params.projectStatuses = joinParam(args.projectStatuses);
821
+ if (args.projectOwnerIds)
822
+ params.projectOwnerIds = joinParam(args.projectOwnerIds);
823
+ if (args.projectIds)
824
+ params.projectIds = joinParam(args.projectIds);
825
+ if (args.projectHealths)
826
+ params.projectHealths = joinParam(args.projectHealths);
827
+ if (args.projectCompanyIds)
828
+ params.projectCompanyIds = joinParam(args.projectCompanyIds);
829
+ if (args.projectCategoryIds)
830
+ params.projectCategoryIds = joinParam(args.projectCategoryIds);
831
+ if (args.includeCustomFieldIds)
832
+ params.includeCustomFieldIds = joinParam(args.includeCustomFieldIds);
833
+ if (args.include)
834
+ params.include = joinParam(args.include);
835
+ if (args.featuresEnabled)
836
+ params.featuresEnabled = joinParam(args.featuresEnabled);
837
+ if (args.excludeTagIds)
838
+ params.excludeTagIds = joinParam(args.excludeTagIds);
839
+ if (args.excludeProjectIds)
840
+ params.excludeProjectIds = joinParam(args.excludeProjectIds);
841
+ // Handle fields arrays
842
+ if (args.fieldsWorkflows)
843
+ params['fields[workflows]'] = joinParam(args.fieldsWorkflows);
844
+ if (args.fieldsUsers)
845
+ params['fields[users]'] = joinParam(args.fieldsUsers);
846
+ if (args.fieldsTags)
847
+ params['fields[tags]'] = joinParam(args.fieldsTags);
848
+ if (args.fieldsStages)
849
+ params['fields[stages]'] = joinParam(args.fieldsStages);
850
+ if (args.fieldsProjects)
851
+ params['fields[projects]'] = joinParam(args.fieldsProjects);
852
+ if (args.fieldsProjectCategories)
853
+ params['fields[projectcategories]'] = joinParam(args.fieldsProjectCategories);
854
+ if (args.fieldsProjectUpdates)
855
+ params['fields[projectUpdates]'] = joinParam(args.fieldsProjectUpdates);
856
+ if (args.fieldsProjectBudgets)
857
+ params['fields[projectBudgets]'] = joinParam(args.fieldsProjectBudgets);
858
+ if (args.fieldsPortfolioColumns)
859
+ params['fields[portfolioColumns]'] = joinParam(args.fieldsPortfolioColumns);
860
+ if (args.fieldsPortfolioCards)
861
+ params['fields[portfolioCards]'] = joinParam(args.fieldsPortfolioCards);
862
+ if (args.fieldsPortfolioBoards)
863
+ params['fields[portfolioBoards]'] = joinParam(args.fieldsPortfolioBoards);
864
+ if (args.fieldsIndustries)
865
+ params['fields[industries]'] = joinParam(args.fieldsIndustries);
866
+ if (args.fieldsCustomFields)
867
+ params['fields[customfields]'] = joinParam(args.fieldsCustomFields);
868
+ if (args.fieldsCustomFieldProjects)
869
+ params['fields[customfieldProjects]'] = joinParam(args.fieldsCustomFieldProjects);
870
+ if (args.fieldsCountries)
871
+ params['fields[countries]'] = joinParam(args.fieldsCountries);
872
+ if (args.fieldsCompanies)
873
+ params['fields[companies]'] = joinParam(args.fieldsCompanies);
874
+ const response = await axiosInstance.get(`/projects/${projectId}.json`, { params });
875
+ return {
876
+ content: [
877
+ {
878
+ type: "text",
879
+ text: JSON.stringify(response.data, null, 2),
880
+ },
881
+ ],
882
+ };
883
+ }
884
+ catch (error) {
885
+ return handleApiError(error);
886
+ }
887
+ });
888
+ server.tool("get_all_project_updates", {
889
+ updatedAfter: z.string().optional().describe("filter by updated after"),
890
+ projectStatus: z.string().optional().describe("filter by project status"),
891
+ orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
892
+ orderBy: z.enum(["date", "color", "health", "project", "user"]).optional().describe("order by"),
893
+ createdAfter: z.string().optional().describe("filter by creation date"),
894
+ projectId: z.number().optional().describe("filter by project id"),
895
+ pageSize: z.number().optional().describe("number of items in a page"),
896
+ page: z.number().optional().describe("page number"),
897
+ skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts"),
898
+ showDeleted: z.boolean().optional().describe("include deleted items"),
899
+ reactions: z.boolean().optional().describe("add reactions to the response"),
900
+ onlyStarredProjects: z.boolean().optional().describe("filter by starred projects only"),
901
+ matchAllProjectTags: z.boolean().optional().describe("match all project tags"),
902
+ includeArchivedProjects: z.boolean().optional().describe("include archived projects"),
903
+ emoji: z.boolean().optional().describe("parse emojis to unicode"),
904
+ activeOnly: z.boolean().optional().describe("filter by active"),
905
+ projectTagIds: z.array(z.number()).optional().describe("filter by project tag ids"),
906
+ projectStatuses: z.array(z.string()).optional().describe("list of project status"),
907
+ projectOwnerIds: z.array(z.number()).optional().describe("filter by project owner ids"),
908
+ projectHealths: z.array(z.number()).optional().describe("filter by project health"),
909
+ projectCompanyIds: z.array(z.number()).optional().describe("filter by company ids"),
910
+ projectCategoryIds: z.array(z.number()).optional().describe("filter by project category ids"),
911
+ include: z.array(z.string()).optional().describe("include"),
912
+ fieldsUsers: z.array(z.string()).optional().describe("fields[users]"),
913
+ fieldsProjects: z.array(z.string()).optional().describe("fields[projects]"),
914
+ fieldsProjectUpdates: z.array(z.string()).optional().describe("fields[projectUpdates]"),
915
+ }, async (args) => {
916
+ try {
917
+ const params = { ...args };
918
+ const joinParam = (arr) => arr ? arr.join(',') : undefined;
919
+ if (args.projectTagIds)
920
+ params.projectTagIds = joinParam(args.projectTagIds);
921
+ if (args.projectStatuses)
922
+ params.projectStatuses = joinParam(args.projectStatuses);
923
+ if (args.projectOwnerIds)
924
+ params.projectOwnerIds = joinParam(args.projectOwnerIds);
925
+ if (args.projectHealths)
926
+ params.projectHealths = joinParam(args.projectHealths);
927
+ if (args.projectCompanyIds)
928
+ params.projectCompanyIds = joinParam(args.projectCompanyIds);
929
+ if (args.projectCategoryIds)
930
+ params.projectCategoryIds = joinParam(args.projectCategoryIds);
931
+ if (args.include)
932
+ params.include = joinParam(args.include);
933
+ if (args.fieldsUsers)
934
+ params['fields[users]'] = joinParam(args.fieldsUsers);
935
+ if (args.fieldsProjects)
936
+ params['fields[projects]'] = joinParam(args.fieldsProjects);
937
+ if (args.fieldsProjectUpdates)
938
+ params['fields[projectUpdates]'] = joinParam(args.fieldsProjectUpdates);
939
+ const response = await axiosInstance.get("/projects/updates.json", { params });
940
+ return {
941
+ content: [
942
+ {
943
+ type: "text",
944
+ text: JSON.stringify(response.data, null, 2),
945
+ },
946
+ ],
947
+ };
948
+ }
949
+ catch (error) {
950
+ return handleApiError(error);
951
+ }
952
+ });
953
+ server.tool("get_project_updates", {
954
+ projectId: z.number().describe("The ID of the project"),
955
+ updatedAfter: z.string().optional().describe("filter by updated after"),
956
+ projectStatus: z.string().optional().describe("filter by project status"),
957
+ orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
958
+ orderBy: z.enum(["date", "color", "health", "project", "user"]).optional().describe("order by"),
959
+ createdAfter: z.string().optional().describe("filter by creation date"),
960
+ pageSize: z.number().optional().describe("number of items in a page"),
961
+ page: z.number().optional().describe("page number"),
962
+ skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts"),
963
+ showDeleted: z.boolean().optional().describe("include deleted items"),
964
+ reactions: z.boolean().optional().describe("add reactions to the response"),
965
+ onlyStarredProjects: z.boolean().optional().describe("filter by starred projects only"),
966
+ matchAllProjectTags: z.boolean().optional().describe("match all project tags"),
967
+ includeArchivedProjects: z.boolean().optional().describe("include archived projects"),
968
+ emoji: z.boolean().optional().describe("parse emojis to unicode"),
969
+ activeOnly: z.boolean().optional().describe("filter by active"),
970
+ projectTagIds: z.array(z.number()).optional().describe("filter by project tag ids"),
971
+ projectStatuses: z.array(z.string()).optional().describe("list of project status"),
972
+ projectOwnerIds: z.array(z.number()).optional().describe("filter by project owner ids"),
973
+ projectHealths: z.array(z.number()).optional().describe("filter by project health"),
974
+ projectCompanyIds: z.array(z.number()).optional().describe("filter by company ids"),
975
+ projectCategoryIds: z.array(z.number()).optional().describe("filter by project category ids"),
976
+ include: z.array(z.string()).optional().describe("include"),
977
+ fieldsUsers: z.array(z.string()).optional().describe("fields[users]"),
978
+ fieldsProjects: z.array(z.string()).optional().describe("fields[projects]"),
979
+ fieldsProjectUpdates: z.array(z.string()).optional().describe("fields[projectUpdates]"),
980
+ }, async (args) => {
981
+ try {
982
+ const { projectId, ...restParams } = args;
983
+ const params = { ...restParams };
984
+ const joinParam = (arr) => arr ? arr.join(',') : undefined;
985
+ if (args.projectTagIds)
986
+ params.projectTagIds = joinParam(args.projectTagIds);
987
+ if (args.projectStatuses)
988
+ params.projectStatuses = joinParam(args.projectStatuses);
989
+ if (args.projectOwnerIds)
990
+ params.projectOwnerIds = joinParam(args.projectOwnerIds);
991
+ if (args.projectHealths)
992
+ params.projectHealths = joinParam(args.projectHealths);
993
+ if (args.projectCompanyIds)
994
+ params.projectCompanyIds = joinParam(args.projectCompanyIds);
995
+ if (args.projectCategoryIds)
996
+ params.projectCategoryIds = joinParam(args.projectCategoryIds);
997
+ if (args.include)
998
+ params.include = joinParam(args.include);
999
+ if (args.fieldsUsers)
1000
+ params['fields[users]'] = joinParam(args.fieldsUsers);
1001
+ if (args.fieldsProjects)
1002
+ params['fields[projects]'] = joinParam(args.fieldsProjects);
1003
+ if (args.fieldsProjectUpdates)
1004
+ params['fields[projectUpdates]'] = joinParam(args.fieldsProjectUpdates);
1005
+ const response = await axiosInstance.get(`/projects/${projectId}/updates.json`, { params });
1006
+ return {
1007
+ content: [
1008
+ {
1009
+ type: "text",
1010
+ text: JSON.stringify(response.data, null, 2),
1011
+ },
1012
+ ],
1013
+ };
1014
+ }
1015
+ catch (error) {
1016
+ return handleApiError(error);
1017
+ }
1018
+ });
1019
+ server.tool("get_all_tags", {
1020
+ updatedAfter: z.string().optional().describe("search for tags updated after the provided date"),
1021
+ searchTerm: z.string().optional().describe("filter by search term"),
1022
+ orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
1023
+ orderBy: z.enum(["name", "count", "project", "color", "datelastupdated", "projectdatelastused"]).optional().describe("order by"),
1024
+ itemType: z.enum(["all", "project", "task", "tasklist", "milestone", "message", "comment", "timelog", "file", "user", "company", "invoice", "risk", "notebook", "link", "event", "notebookversion", "fileversion"]).optional().describe("filter by item type"),
1025
+ filter: z.enum(["all", "recent"]).optional().describe("mode used when filtering the tags"),
1026
+ pageSize: z.number().optional().describe("number of items in a page"),
1027
+ page: z.number().optional().describe("page number"),
1028
+ withCounters: z.boolean().optional().describe("include in the response the number of items that use the tag"),
1029
+ skipSpecial: z.boolean().optional().describe("do not include in the response special tags"),
1030
+ skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts"),
1031
+ searchRightOnly: z.boolean().optional().describe("search term will be placed as a prefix to match the tag names"),
1032
+ skipIds: z.array(z.number()).optional().describe("skip from the result tags with the defined ids"),
1033
+ projectIds: z.array(z.number()).optional().describe("filter by projects"),
1034
+ include: z.array(z.string()).optional().describe("include"),
1035
+ ids: z.array(z.number()).optional().describe("filter by ids"),
1036
+ fieldsTags: z.array(z.string()).optional().describe("fields[tags]"),
1037
+ }, async (args) => {
1038
+ try {
1039
+ const params = { ...args };
1040
+ const joinParam = (arr) => arr ? arr.join(',') : undefined;
1041
+ if (args.skipIds)
1042
+ params.skipIds = joinParam(args.skipIds);
1043
+ if (args.projectIds)
1044
+ params.projectIds = joinParam(args.projectIds);
1045
+ if (args.include)
1046
+ params.include = joinParam(args.include);
1047
+ if (args.ids)
1048
+ params.ids = joinParam(args.ids);
1049
+ if (args.fieldsTags)
1050
+ params['fields[tags]'] = joinParam(args.fieldsTags);
1051
+ const response = await axiosInstance.get("/tags.json", { params });
1052
+ return {
1053
+ content: [
1054
+ {
1055
+ type: "text",
1056
+ text: JSON.stringify(response.data, null, 2),
1057
+ },
1058
+ ],
1059
+ };
1060
+ }
1061
+ catch (error) {
1062
+ return handleApiError(error);
1063
+ }
1064
+ });
1065
+ server.tool("get_tag", {
1066
+ tagId: z.number().describe("The ID of the tag"),
1067
+ }, async ({ tagId }) => {
1068
+ try {
1069
+ const response = await axiosInstance.get(`/tags/${tagId}.json`);
1070
+ return {
1071
+ content: [
1072
+ {
1073
+ type: "text",
1074
+ text: JSON.stringify(response.data, null, 2),
1075
+ },
1076
+ ],
1077
+ };
1078
+ }
1079
+ catch (error) {
1080
+ return handleApiError(error);
1081
+ }
1082
+ });
606
1083
  async function main() {
607
1084
  const transport = new StdioServerTransport();
608
1085
  await server.connect(transport);
package/package.json CHANGED
@@ -3,8 +3,8 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.0.2",
7
- "description": "MCP Server for Teamwork",
6
+ "version": "1.1.0",
7
+ "description": "MCP Server for Teamwork API V3 - Projects, Time Entries, People, Companies, Tags, and Updates",
8
8
  "main": "dist/index.js",
9
9
  "bin": {
10
10
  "teamwork-mcp": "./dist/index.js"
@@ -16,7 +16,8 @@
16
16
  "scripts": {
17
17
  "build": "tsc",
18
18
  "start": "node dist/index.js",
19
- "dev": "tsc --watch"
19
+ "dev": "tsc --watch",
20
+ "prepublishOnly": "npm run build"
20
21
  },
21
22
  "keywords": [
22
23
  "mcp",