@jorgeluismlima/teamwork-mcp 1.0.0 → 1.0.2
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/README.md +34 -22
- package/dist/index.js +274 -29
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -7,14 +7,19 @@ A Model Context Protocol (MCP) server that connects to the Teamwork API, allowin
|
|
|
7
7
|
This server exposes the following tools:
|
|
8
8
|
|
|
9
9
|
### Projects & Time Tracking
|
|
10
|
-
* **`list_projects`**:
|
|
10
|
+
* **`list_projects`**: Comprehensive project listing with 50+ filters (date, health, status, tags, etc.) and full API response support.
|
|
11
11
|
* **`create_time_entry`**: Creates a time entry (timelog) for a specific project.
|
|
12
|
-
* **`get_time_entries`**: Retrieves time entries
|
|
12
|
+
* **`get_time_entries`**: Retrieves detailed time entries with 60+ filters (billed status, users, tags, etc.).
|
|
13
13
|
* **`total_count_of_active_projects`**: Returns the total count of active projects.
|
|
14
14
|
* **`total_billable_time_per_project`**: Returns total billable minutes per project (optional date range).
|
|
15
15
|
* **`get_health_stats`**: Returns project health metrics (Good, Bad, OK, etc.), with various filters.
|
|
16
16
|
* **`get_project_time_totals`**: Returns total hours/minutes for a project (optionally filtered by user).
|
|
17
17
|
|
|
18
|
+
### Project Categories
|
|
19
|
+
* **`list_of_project_categories`**: Lists all project categories with filtering options.
|
|
20
|
+
* **`get_specific_project_category`**: Retrieves details of a specific category.
|
|
21
|
+
* **`list_of_teamwork_project_categories`**: Helper tool to list categories.
|
|
22
|
+
|
|
18
23
|
### People & Companies
|
|
19
24
|
* **`get_project_people`**: Lists all people associated with a project.
|
|
20
25
|
* **`get_person`**: Retrieves detailed information about a specific person (user).
|
|
@@ -37,34 +42,22 @@ Before using this MCP server, ensure you have:
|
|
|
37
42
|
* **Teamwork Account**: You must have an active Teamwork account.
|
|
38
43
|
* **API Credentials**: You need your Teamwork Site Name and an API Key (or password) to authenticate.
|
|
39
44
|
|
|
40
|
-
##
|
|
45
|
+
## Quick Start
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm install
|
|
45
|
-
```
|
|
47
|
+
You can run this server directly using `npx` without installing it globally or cloning the repository.
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npm run build
|
|
50
|
-
```
|
|
49
|
+
### Client Configuration (Claude Desktop / IDEs)
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
node dist/index.js
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Client Configuration (Example)
|
|
58
|
-
|
|
59
|
-
To add this server to an MCP client (like Claude Desktop or an IDE extension), add the following to your MCP configuration file:
|
|
51
|
+
To add this server to an MCP client, simply add the following to your MCP configuration file:
|
|
60
52
|
|
|
61
53
|
```json
|
|
62
54
|
{
|
|
63
55
|
"mcpServers": {
|
|
64
56
|
"teamwork": {
|
|
65
|
-
"command": "
|
|
57
|
+
"command": "npx",
|
|
66
58
|
"args": [
|
|
67
|
-
"
|
|
59
|
+
"-y",
|
|
60
|
+
"@jorgeluismlima/teamwork-mcp"
|
|
68
61
|
],
|
|
69
62
|
"env": {
|
|
70
63
|
"TEAMWORK_SITE_NAME": "your-site-name",
|
|
@@ -76,4 +69,23 @@ To add this server to an MCP client (like Claude Desktop or an IDE extension), a
|
|
|
76
69
|
}
|
|
77
70
|
```
|
|
78
71
|
|
|
79
|
-
|
|
72
|
+
## Local Development & Installation
|
|
73
|
+
|
|
74
|
+
If you want to modify the code or run it locally from the source:
|
|
75
|
+
|
|
76
|
+
1. **Clone and Install:**
|
|
77
|
+
```bash
|
|
78
|
+
git clone <repository-url>
|
|
79
|
+
cd teamwork-mcp
|
|
80
|
+
npm install
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
2. **Build the project:**
|
|
84
|
+
```bash
|
|
85
|
+
npm run build
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
3. **Run the server:**
|
|
89
|
+
```bash
|
|
90
|
+
node dist/index.js
|
|
91
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -57,30 +57,105 @@ const handleApiError = (error) => {
|
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
59
|
server.tool("list_projects", {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
updatedAfter: z.string().optional().describe("updated after"),
|
|
61
|
+
timeMode: z.enum(["timelogs", "estimated"]).optional().describe("profitability time mode"),
|
|
62
|
+
searchTerm: z.string().optional().describe("filter by project name"),
|
|
63
|
+
reportType: z.enum(["project", "health"]).optional().describe("define the type of the report"),
|
|
64
|
+
reportTimezone: z.string().optional().describe("Optional to configure the report dates displayed in a timezone"),
|
|
65
|
+
reportFormat: z.enum(["csv", "html", "pdf", "xls"]).optional().describe("define the format of the report"),
|
|
66
|
+
projectType: z.string().optional().describe("filter by project type"),
|
|
67
|
+
orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
|
|
68
|
+
orderBy: z.enum(["companyname", "datecreated", "duedate", "lastactivity", "name", "namecaseinsensitive", "ownercompany", "starred", "categoryname"]).optional().describe("order by"),
|
|
69
|
+
notCompletedBefore: z.string().optional().describe("filter by projects that have not been completed before the given date"),
|
|
70
|
+
minLastActivityDate: z.string().optional().describe("filter by min last activity date"),
|
|
71
|
+
maxLastActivityDate: z.string().optional().describe("filter by max last activity date"),
|
|
72
|
+
userId: z.number().optional().describe("filter by user id"),
|
|
73
|
+
pageSize: z.number().optional().describe("number of items in a page"),
|
|
74
|
+
page: z.number().optional().describe("page number"),
|
|
75
|
+
orderByCustomFieldId: z.number().optional().describe("order by custom field id when orderBy is equal to customfield"),
|
|
76
|
+
minBudgetCapacityUsedPercent: z.number().optional().describe("filter by minimum budget capacity used"),
|
|
77
|
+
maxBudgetCapacityUsedPercent: z.number().optional().describe("filter by maximum budget capacity used"),
|
|
78
|
+
useFormulaFields: z.boolean().optional().describe("use formula fields"),
|
|
79
|
+
skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts on a list API endpoint"),
|
|
80
|
+
searchCompanies: z.boolean().optional().describe("include companies in the search"),
|
|
81
|
+
searchByLetter: z.boolean().optional().describe("search projects beginning with the search term character only"),
|
|
82
|
+
onlyStarredProjects: z.boolean().optional().describe("filter by starred projects only"),
|
|
83
|
+
onlyProjectsWithExplicitMembership: z.boolean().optional().describe("only show projects with explicit membership"),
|
|
84
|
+
onlyProjectsThatCanLogTime: z.boolean().optional().describe("can log time on projects"),
|
|
85
|
+
onlyProjectsThatCanAddTasks: z.boolean().optional().describe("can add tasks on projects"),
|
|
86
|
+
onlyArchivedProjects: z.boolean().optional().describe("return only archived projects"),
|
|
87
|
+
matchAllProjectTags: z.boolean().optional().describe("match all project tags"),
|
|
88
|
+
matchAllExcludedTags: z.boolean().optional().describe("match all excluded project tags"),
|
|
89
|
+
isReportDownload: z.boolean().optional().describe("generate a report document"),
|
|
90
|
+
includeTentativeProjects: z.boolean().optional().describe("include alongside normal projects, tentative ones"),
|
|
91
|
+
includeTabSystemStatus: z.boolean().optional().describe("include tab system status in response"),
|
|
92
|
+
includeSubCategories: z.boolean().optional().describe("include sub categories when filtering by ids"),
|
|
93
|
+
includeStats: z.boolean().optional().describe("include project status counts"),
|
|
94
|
+
includeProjectUserInfo: z.boolean().optional().describe("fetch user-specific data such as isStarred"),
|
|
95
|
+
includeProjectProfitability: z.boolean().optional().describe("include project profitability in response"),
|
|
96
|
+
includeProjectDates: z.boolean().optional().describe("include minimum and maximum start/end dates for projects"),
|
|
97
|
+
includeCustomFields: z.boolean().optional().describe("include custom fields"),
|
|
98
|
+
includeCounts: z.boolean().optional().describe("include project related counts"),
|
|
99
|
+
includeCompletedStatus: z.boolean().optional().describe("include completed projects when filtering by project statuses current,late"),
|
|
100
|
+
includeArchivedProjects: z.boolean().optional().describe("include archived projects"),
|
|
101
|
+
hideObservedProjects: z.boolean().optional().describe("hide projects where the logged-in user is just an observer"),
|
|
102
|
+
alwaysIncludeFiltering: z.boolean().optional().describe("includes filters when project ids are provided"),
|
|
103
|
+
usersWithExplicitMembershipIds: z.array(z.number()).optional().describe("only show projects that have an explicit common membership with provided user ids"),
|
|
104
|
+
teamIds: z.array(z.number()).optional().describe("filter by projects that contain users associated with the team ids"),
|
|
105
|
+
selectedColumns: z.array(z.string()).optional().describe("select the columns to use in exports"),
|
|
106
|
+
projectTagIds: z.array(z.number()).optional().describe("filter by project tag ids"),
|
|
107
|
+
projectStatuses: z.array(z.string()).optional().describe("filter by project status (active, current, late, upcoming, completed, deleted)"),
|
|
108
|
+
projectOwnerIds: z.array(z.number()).optional().describe("filter by project owner ids"),
|
|
109
|
+
projectIds: z.array(z.number()).optional().describe("filter by project ids"),
|
|
110
|
+
projectHealths: z.array(z.number()).optional().describe("filter by project healths (0: not set, 1: bad, 2: ok, 3: good)"),
|
|
111
|
+
projectCompanyIds: z.array(z.number()).optional().describe("filter by company ids"),
|
|
112
|
+
projectCategoryIds: z.array(z.number()).optional().describe("filter by project category ids"),
|
|
113
|
+
includeCustomFieldIds: z.array(z.number()).optional().describe("include specific custom fields"),
|
|
114
|
+
include: z.array(z.string()).optional().describe("include related data"),
|
|
115
|
+
featuresEnabled: z.array(z.string()).optional().describe("filter by projects that have features enabled"),
|
|
116
|
+
excludeTagIds: z.array(z.number()).optional().describe("exclude by project tag ids"),
|
|
117
|
+
excludeProjectIds: z.array(z.number()).optional().describe("exclude certain project ids"),
|
|
118
|
+
}, async (args) => {
|
|
63
119
|
try {
|
|
64
|
-
const params = {};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
params.
|
|
120
|
+
const params = { ...args };
|
|
121
|
+
// Helper to join arrays
|
|
122
|
+
const joinParam = (arr) => arr ? arr.join(',') : undefined;
|
|
123
|
+
if (args.usersWithExplicitMembershipIds)
|
|
124
|
+
params.usersWithExplicitMembershipIds = joinParam(args.usersWithExplicitMembershipIds);
|
|
125
|
+
if (args.teamIds)
|
|
126
|
+
params.teamIds = joinParam(args.teamIds);
|
|
127
|
+
if (args.selectedColumns)
|
|
128
|
+
params.selectedColumns = joinParam(args.selectedColumns);
|
|
129
|
+
if (args.projectTagIds)
|
|
130
|
+
params.projectTagIds = joinParam(args.projectTagIds);
|
|
131
|
+
if (args.projectStatuses)
|
|
132
|
+
params.projectStatuses = joinParam(args.projectStatuses);
|
|
133
|
+
if (args.projectOwnerIds)
|
|
134
|
+
params.projectOwnerIds = joinParam(args.projectOwnerIds);
|
|
135
|
+
if (args.projectIds)
|
|
136
|
+
params.projectIds = joinParam(args.projectIds);
|
|
137
|
+
if (args.projectHealths)
|
|
138
|
+
params.projectHealths = joinParam(args.projectHealths);
|
|
139
|
+
if (args.projectCompanyIds)
|
|
140
|
+
params.projectCompanyIds = joinParam(args.projectCompanyIds);
|
|
141
|
+
if (args.projectCategoryIds)
|
|
142
|
+
params.projectCategoryIds = joinParam(args.projectCategoryIds);
|
|
143
|
+
if (args.includeCustomFieldIds)
|
|
144
|
+
params.includeCustomFieldIds = joinParam(args.includeCustomFieldIds);
|
|
145
|
+
if (args.include)
|
|
146
|
+
params.include = joinParam(args.include);
|
|
147
|
+
if (args.featuresEnabled)
|
|
148
|
+
params.featuresEnabled = joinParam(args.featuresEnabled);
|
|
149
|
+
if (args.excludeTagIds)
|
|
150
|
+
params.excludeTagIds = joinParam(args.excludeTagIds);
|
|
151
|
+
if (args.excludeProjectIds)
|
|
152
|
+
params.excludeProjectIds = joinParam(args.excludeProjectIds);
|
|
69
153
|
const response = await axiosInstance.get("/projects.json", { params });
|
|
70
|
-
const simplifiedProjects = response.data.projects.map((p) => ({
|
|
71
|
-
id: p.id,
|
|
72
|
-
name: p.name,
|
|
73
|
-
description: p.description,
|
|
74
|
-
status: p.status,
|
|
75
|
-
company: p.company,
|
|
76
|
-
createdOn: p.createdOn,
|
|
77
|
-
lastUpdated: p.lastUpdated
|
|
78
|
-
}));
|
|
79
154
|
return {
|
|
80
155
|
content: [
|
|
81
156
|
{
|
|
82
157
|
type: "text",
|
|
83
|
-
text: JSON.stringify(
|
|
158
|
+
text: JSON.stringify(response.data, null, 2),
|
|
84
159
|
},
|
|
85
160
|
],
|
|
86
161
|
};
|
|
@@ -129,18 +204,103 @@ server.tool("create_time_entry", {
|
|
|
129
204
|
});
|
|
130
205
|
server.tool("get_time_entries", {
|
|
131
206
|
projectId: z.number().describe("The ID of the project"),
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
207
|
+
updatedAfter: z.string().optional().describe("filter by updated after date"),
|
|
208
|
+
startDate: z.string().optional().describe("filter by a starting date"),
|
|
209
|
+
reportFormat: z.string().optional().describe("define the format of the report"),
|
|
210
|
+
projectStatus: z.string().optional().describe("filter by project status (active, current, late, upcoming, completed, deleted)"),
|
|
211
|
+
orderMode: z.enum(["asc", "desc"]).optional().describe("order mode"),
|
|
212
|
+
orderBy: z.enum(["company", "date", "dateupdated", "project", "task", "tasklist", "user", "description", "billed", "billable", "timespent"]).optional().describe("sort order"),
|
|
213
|
+
invoicedType: z.enum(["all", "invoiced", "noninvoiced"]).optional().describe("filter by invoiced type"),
|
|
214
|
+
endDate: z.string().optional().describe("filter by an ending date"),
|
|
215
|
+
deletedAfter: z.string().optional().describe("filter by deleted after date"),
|
|
216
|
+
billableType: z.enum(["all", "billable", "non-billable"]).optional().describe("filter by billable type"),
|
|
217
|
+
updatedBy: z.number().optional().describe("filter by the user who updated the timelog"),
|
|
218
|
+
ticketId: z.number().optional().describe("filter by ticket id"),
|
|
219
|
+
tasklistId: z.number().optional().describe("filter by tasklist id"),
|
|
220
|
+
taskId: z.number().optional().describe("filter by task id (deprecated, use taskIds)"),
|
|
221
|
+
pageSize: z.number().optional().describe("number of items in a page"),
|
|
222
|
+
page: z.number().optional().describe("page number"),
|
|
223
|
+
invoiceId: z.number().optional().describe("filter by invoice id"),
|
|
224
|
+
budgetId: z.number().optional().describe("filter by budget id"),
|
|
225
|
+
allocationId: z.number().optional().describe("filter by allocation id"),
|
|
226
|
+
useFallbackMethod: z.boolean().optional().describe("use fallback method"),
|
|
227
|
+
unattachedTimelogs: z.boolean().optional().describe("filter by timelogs that are directly logged against projects"),
|
|
228
|
+
skipCounts: z.boolean().optional().describe("SkipCounts allows you to skip doing counts"),
|
|
229
|
+
showDeleted: z.boolean().optional().describe("include deleted items"),
|
|
230
|
+
returnCostInfo: z.boolean().optional().describe("used to return the cost rate and total"),
|
|
231
|
+
returnBillableInfo: z.boolean().optional().describe("used to return the billable rate and total"),
|
|
232
|
+
onlyStarredProjects: z.boolean().optional().describe("filter by starred projects only"),
|
|
233
|
+
matchAllTaskTags: z.boolean().optional().describe("match all task tags"),
|
|
234
|
+
matchAllTags: z.boolean().optional().describe("match all tags"),
|
|
235
|
+
matchAllProjectTags: z.boolean().optional().describe("match all project tags"),
|
|
236
|
+
isReportDownload: z.boolean().optional().describe("generate a report document"),
|
|
237
|
+
includeTotals: z.boolean().optional().describe("include totals"),
|
|
238
|
+
includeTentativeProjects: z.boolean().optional().describe("include tentative projects"),
|
|
239
|
+
includePermissions: z.boolean().optional().describe("include permissions"),
|
|
240
|
+
includeDescendants: z.boolean().optional().describe("include descendants"),
|
|
241
|
+
includeArchivedProjects: z.boolean().optional().describe("include archived projects"),
|
|
242
|
+
deletedOnly: z.boolean().optional().describe("only deleted tasks"),
|
|
243
|
+
checkLocking: z.boolean().optional().describe("check timesheets' locking"),
|
|
244
|
+
taskTagIds: z.array(z.number()).optional().describe("filter by task tag ids"),
|
|
245
|
+
taskStatuses: z.array(z.string()).optional().describe("filter by task statuses"),
|
|
246
|
+
taskIds: z.array(z.number()).optional().describe("filter by task ids"),
|
|
247
|
+
tagIds: z.array(z.number()).optional().describe("filter by tag ids"),
|
|
248
|
+
selectedColumns: z.array(z.string()).optional().describe("customise the report by selecting columns"),
|
|
249
|
+
projectTagIds: z.array(z.number()).optional().describe("filter by project tag ids"),
|
|
250
|
+
projectStatuses: z.array(z.string()).optional().describe("filter by project statuses"),
|
|
251
|
+
projectOwnerIds: z.array(z.number()).optional().describe("filter by project owner ids"),
|
|
252
|
+
projectIds: z.array(z.number()).optional().describe("filter by project ids"),
|
|
253
|
+
projectHealths: z.array(z.number()).optional().describe("filter by project healths (0: not set, 1: bad, 2: ok, 3: good)"),
|
|
254
|
+
projectCompanyIds: z.array(z.number()).optional().describe("filter by project company ids"),
|
|
255
|
+
projectCategoryIds: z.array(z.number()).optional().describe("filter by project category ids"),
|
|
256
|
+
include: z.array(z.string()).optional().describe("include related data"),
|
|
257
|
+
ids: z.array(z.number()).optional().describe("filter by ids"),
|
|
258
|
+
assignedToUserIds: z.array(z.number()).optional().describe("filter by assigned user ids"),
|
|
259
|
+
assignedToTeamIds: z.array(z.number()).optional().describe("filter by assigned team ids"),
|
|
260
|
+
assignedToCompanyIds: z.array(z.number()).optional().describe("filter by assigned company ids"),
|
|
261
|
+
assignedTeamIds: z.array(z.number()).optional().describe("filter by assigned team ids"),
|
|
262
|
+
}, async (args) => {
|
|
136
263
|
try {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
264
|
+
const { projectId, ...restParams } = args;
|
|
265
|
+
const params = { ...restParams };
|
|
266
|
+
// Helper to join arrays
|
|
267
|
+
const joinParam = (arr) => arr ? arr.join(',') : undefined;
|
|
268
|
+
if (args.taskTagIds)
|
|
269
|
+
params.taskTagIds = joinParam(args.taskTagIds);
|
|
270
|
+
if (args.taskStatuses)
|
|
271
|
+
params.taskStatuses = joinParam(args.taskStatuses);
|
|
272
|
+
if (args.taskIds)
|
|
273
|
+
params.taskIds = joinParam(args.taskIds);
|
|
274
|
+
if (args.tagIds)
|
|
275
|
+
params.tagIds = joinParam(args.tagIds);
|
|
276
|
+
if (args.selectedColumns)
|
|
277
|
+
params.selectedColumns = joinParam(args.selectedColumns);
|
|
278
|
+
if (args.projectTagIds)
|
|
279
|
+
params.projectTagIds = joinParam(args.projectTagIds);
|
|
280
|
+
if (args.projectStatuses)
|
|
281
|
+
params.projectStatuses = joinParam(args.projectStatuses);
|
|
282
|
+
if (args.projectOwnerIds)
|
|
283
|
+
params.projectOwnerIds = joinParam(args.projectOwnerIds);
|
|
284
|
+
if (args.projectIds)
|
|
285
|
+
params.projectIds = joinParam(args.projectIds);
|
|
286
|
+
if (args.projectHealths)
|
|
287
|
+
params.projectHealths = joinParam(args.projectHealths);
|
|
288
|
+
if (args.projectCompanyIds)
|
|
289
|
+
params.projectCompanyIds = joinParam(args.projectCompanyIds);
|
|
290
|
+
if (args.projectCategoryIds)
|
|
291
|
+
params.projectCategoryIds = joinParam(args.projectCategoryIds);
|
|
292
|
+
if (args.include)
|
|
293
|
+
params.include = joinParam(args.include);
|
|
294
|
+
if (args.ids)
|
|
295
|
+
params.ids = joinParam(args.ids);
|
|
296
|
+
if (args.assignedToUserIds)
|
|
297
|
+
params.assignedToUserIds = joinParam(args.assignedToUserIds);
|
|
298
|
+
if (args.assignedToTeamIds)
|
|
299
|
+
params.assignedToTeamIds = joinParam(args.assignedToTeamIds);
|
|
300
|
+
if (args.assignedToCompanyIds)
|
|
301
|
+
params.assignedToCompanyIds = joinParam(args.assignedToCompanyIds);
|
|
302
|
+
if (args.assignedTeamIds)
|
|
303
|
+
params.assignedTeamIds = joinParam(args.assignedTeamIds);
|
|
144
304
|
const response = await axiosInstance.get(`/projects/${projectId}/time.json`, { params });
|
|
145
305
|
return {
|
|
146
306
|
content: [
|
|
@@ -358,6 +518,91 @@ server.tool("get_company", {
|
|
|
358
518
|
return handleApiError(error);
|
|
359
519
|
}
|
|
360
520
|
});
|
|
521
|
+
server.tool("list_of_project_categories", {
|
|
522
|
+
searchTerm: z.string().optional().describe("Filter by name"),
|
|
523
|
+
onlyStarredProjects: z.boolean().optional().describe("Filter by starred projects only"),
|
|
524
|
+
projectStatuses: z.array(z.string()).optional().describe("Filter by project statuses (Comma separated values)"),
|
|
525
|
+
ids: z.array(z.number()).optional().describe("Filter by specific ids"),
|
|
526
|
+
fields: z.array(z.string()).optional().describe("Fields to return: id, name, color, count, parent, parentId"),
|
|
527
|
+
}, async (args) => {
|
|
528
|
+
try {
|
|
529
|
+
const params = {};
|
|
530
|
+
if (args.searchTerm)
|
|
531
|
+
params.searchTerm = args.searchTerm;
|
|
532
|
+
if (args.onlyStarredProjects !== undefined)
|
|
533
|
+
params.onlyStarredProjects = args.onlyStarredProjects;
|
|
534
|
+
const joinParam = (arr) => arr ? arr.join(',') : undefined;
|
|
535
|
+
if (args.projectStatuses)
|
|
536
|
+
params.projectStatuses = joinParam(args.projectStatuses);
|
|
537
|
+
if (args.ids)
|
|
538
|
+
params.ids = joinParam(args.ids);
|
|
539
|
+
if (args.fields)
|
|
540
|
+
params['fields[projectcategories]'] = joinParam(args.fields);
|
|
541
|
+
const response = await axiosInstance.get("/projectcategories.json", { params });
|
|
542
|
+
return {
|
|
543
|
+
content: [
|
|
544
|
+
{
|
|
545
|
+
type: "text",
|
|
546
|
+
text: JSON.stringify(response.data, null, 2),
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
return handleApiError(error);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
server.tool("get_specific_project_category", {
|
|
556
|
+
id: z.number().describe("The ID of the category"),
|
|
557
|
+
searchTerm: z.string().optional().describe("Filter by name"),
|
|
558
|
+
onlyStarredProjects: z.boolean().optional().describe("Filter by starred projects only"),
|
|
559
|
+
projectStatuses: z.array(z.string()).optional().describe("Filter by project statuses"),
|
|
560
|
+
ids: z.array(z.number()).optional().describe("Filter by specific ids"),
|
|
561
|
+
fields: z.array(z.string()).optional().describe("Fields to return"),
|
|
562
|
+
}, async (args) => {
|
|
563
|
+
try {
|
|
564
|
+
const params = {};
|
|
565
|
+
if (args.searchTerm)
|
|
566
|
+
params.searchTerm = args.searchTerm;
|
|
567
|
+
if (args.onlyStarredProjects !== undefined)
|
|
568
|
+
params.onlyStarredProjects = args.onlyStarredProjects;
|
|
569
|
+
const joinParam = (arr) => arr ? arr.join(',') : undefined;
|
|
570
|
+
if (args.projectStatuses)
|
|
571
|
+
params.projectStatuses = joinParam(args.projectStatuses);
|
|
572
|
+
if (args.ids)
|
|
573
|
+
params.ids = joinParam(args.ids);
|
|
574
|
+
if (args.fields)
|
|
575
|
+
params['fields[projectcategories]'] = joinParam(args.fields);
|
|
576
|
+
const response = await axiosInstance.get(`/projectcategories/${args.id}.json`, { params });
|
|
577
|
+
return {
|
|
578
|
+
content: [
|
|
579
|
+
{
|
|
580
|
+
type: "text",
|
|
581
|
+
text: JSON.stringify(response.data, null, 2),
|
|
582
|
+
},
|
|
583
|
+
],
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
return handleApiError(error);
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
server.tool("list_of_teamwork_project_categories", {}, async () => {
|
|
591
|
+
try {
|
|
592
|
+
const response = await axiosInstance.get("/projects/teamwork/categories.json");
|
|
593
|
+
return {
|
|
594
|
+
content: [
|
|
595
|
+
{
|
|
596
|
+
type: "text",
|
|
597
|
+
text: JSON.stringify(response.data, null, 2),
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
catch (error) {
|
|
603
|
+
return handleApiError(error);
|
|
604
|
+
}
|
|
605
|
+
});
|
|
361
606
|
async function main() {
|
|
362
607
|
const transport = new StdioServerTransport();
|
|
363
608
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.2",
|
|
7
7
|
"description": "MCP Server for Teamwork",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"bin": {
|
|
@@ -18,8 +18,16 @@
|
|
|
18
18
|
"start": "node dist/index.js",
|
|
19
19
|
"dev": "tsc --watch"
|
|
20
20
|
},
|
|
21
|
-
"keywords": [
|
|
22
|
-
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"teamwork",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"ai",
|
|
26
|
+
"agent",
|
|
27
|
+
"llm",
|
|
28
|
+
"api"
|
|
29
|
+
],
|
|
30
|
+
"author": "Jorge Luis M. Lima",
|
|
23
31
|
"license": "ISC",
|
|
24
32
|
"dependencies": {
|
|
25
33
|
"@modelcontextprotocol/sdk": "^1.0.1",
|