targetprocess-mcp-server 2.2.13 → 2.3.1

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 (37) hide show
  1. package/README.md +39 -0
  2. package/build/config.js +0 -0
  3. package/build/handlers/add_comment.js +14 -0
  4. package/build/handlers/create_bug.js +14 -0
  5. package/build/handlers/create_feature.js +14 -0
  6. package/build/handlers/create_task.js +14 -0
  7. package/build/handlers/create_user_story.js +14 -0
  8. package/build/handlers/get_bug_comments.js +45 -0
  9. package/build/handlers/get_bug_content.js +37 -0
  10. package/build/handlers/get_card_current_status.js +23 -0
  11. package/build/handlers/get_commit_message.js +42 -0
  12. package/build/handlers/get_current_releases.js +20 -0
  13. package/build/handlers/get_feature_user_stories.js +23 -0
  14. package/build/handlers/get_in_progress_tasks_and_bugs.js +35 -0
  15. package/build/handlers/get_logged_in_user.js +23 -0
  16. package/build/handlers/get_my_time_logs.js +20 -0
  17. package/build/handlers/get_projects.js +23 -0
  18. package/build/handlers/get_release_bugs.js +20 -0
  19. package/build/handlers/get_release_features.js +20 -0
  20. package/build/handlers/get_release_open_bugs.js +20 -0
  21. package/build/handlers/get_release_open_user_stories.js +20 -0
  22. package/build/handlers/get_release_user_stories.js +20 -0
  23. package/build/handlers/get_teams.js +41 -0
  24. package/build/handlers/get_user_by_id.js +14 -0
  25. package/build/handlers/get_user_story_bugs.js +23 -0
  26. package/build/handlers/get_user_story_comments.js +45 -0
  27. package/build/handlers/get_user_story_content.js +44 -0
  28. package/build/handlers/get_users.js +20 -0
  29. package/build/handlers/list_my_bugs.js +23 -0
  30. package/build/handlers/list_my_user_stories.js +23 -0
  31. package/build/handlers/log_time.js +14 -0
  32. package/build/handlers/update_bug.js +14 -0
  33. package/build/handlers/update_user_story_sub_state.js +14 -0
  34. package/build/index.js +153 -763
  35. package/build/tp.js +84 -0
  36. package/build/types.js +0 -0
  37. package/package.json +12 -7
package/build/index.js CHANGED
@@ -5,6 +5,37 @@ import { JSDOM } from "jsdom";
5
5
  import { TpClient } from "./tp.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
  import { config } from "./config.js";
8
+ import { handleGetProjects } from "./handlers/get_projects.js";
9
+ import { handleGetUserById } from "./handlers/get_user_by_id.js";
10
+ import { handleGetCurrentReleases } from "./handlers/get_current_releases.js";
11
+ import { handleGetBugContent } from "./handlers/get_bug_content.js";
12
+ import { handleGetLoggedInUser } from "./handlers/get_logged_in_user.js";
13
+ import { handleGetUserStoryContent } from "./handlers/get_user_story_content.js";
14
+ import { handleGetCommitMessage } from "./handlers/get_commit_message.js";
15
+ import { handleGetReleaseUserStories } from "./handlers/get_release_user_stories.js";
16
+ import { handleGetReleaseBugs } from "./handlers/get_release_bugs.js";
17
+ import { handleGetReleaseFeatures } from "./handlers/get_release_features.js";
18
+ import { handleGetReleaseOpenBugs } from "./handlers/get_release_open_bugs.js";
19
+ import { handleGetReleaseOpenUserStories } from "./handlers/get_release_open_user_stories.js";
20
+ import { handleGetUsers } from "./handlers/get_users.js";
21
+ import { handleGetTeams, handleGetTeamsAndTeamAssignments } from "./handlers/get_teams.js";
22
+ import { handleAddComment } from "./handlers/add_comment.js";
23
+ import { handleGetUserStoryComments } from "./handlers/get_user_story_comments.js";
24
+ import { handleGetBugComments } from "./handlers/get_bug_comments.js";
25
+ import { handleCreateBug } from "./handlers/create_bug.js";
26
+ import { handleCreateUserStory } from "./handlers/create_user_story.js";
27
+ import { handleCreateFeature } from "./handlers/create_feature.js";
28
+ import { handleCreateTask } from "./handlers/create_task.js";
29
+ import { handleUpdateBug } from "./handlers/update_bug.js";
30
+ import { handleGetInProgressTasksAndBugs } from "./handlers/get_in_progress_tasks_and_bugs.js";
31
+ import { handleListMyUserStories } from "./handlers/list_my_user_stories.js";
32
+ import { handleListMyBugs } from "./handlers/list_my_bugs.js";
33
+ import { handleLogTime } from "./handlers/log_time.js";
34
+ import { handleGetMyTimeLogs } from "./handlers/get_my_time_logs.js";
35
+ import { handleGetFeatureUserStories } from "./handlers/get_feature_user_stories.js";
36
+ import { handleGetUserStoryBugs } from "./handlers/get_user_story_bugs.js";
37
+ import { handleGetCardCurrentStatus } from "./handlers/get_card_current_status.js";
38
+ import { handleUpdateUserStorySubState } from "./handlers/update_user_story_sub_state.js";
8
39
  const server = new McpServer({
9
40
  name: "tp",
10
41
  version: "1.0.0"
@@ -32,80 +63,11 @@ server.registerTool('get_user_story_content', {
32
63
  .max(6)
33
64
  .describe('TP (or tp) ID (e.g. 145789)')
34
65
  },
35
- }, async ({ id }) => {
36
- const userStory = await tp.getUserStory(id);
37
- if (!userStory) {
38
- return {
39
- content: [{
40
- type: 'text',
41
- text: `Failed to get user story, id: ${id}\n JSON: ${JSON.stringify(userStory, null, 2)}`
42
- }],
43
- };
44
- }
45
- const description = userStory.Description || '';
46
- if (!description) {
47
- return {
48
- content: [{
49
- type: "text",
50
- text: `No description for ${id} tp card`,
51
- }],
52
- };
53
- }
54
- let userStoryResults = {
55
- name: userStory.Name,
56
- id: userStory.Id,
57
- description: '',
58
- feature: userStory.Feature?.Name,
59
- featureId: userStory.Feature?.Id,
60
- customFields: userStory.CustomFields,
61
- };
62
- try {
63
- const dom = new JSDOM(`<html><body><div id="content">${description}</div></body></html>`);
64
- const descriptionText = dom.window.document.getElementById('content')?.textContent;
65
- if (descriptionText) {
66
- userStoryResults.description = descriptionText;
67
- }
68
- }
69
- catch (error) {
70
- console.error("Error parsing user story description:", error);
71
- console.error("Returning user story without description");
72
- }
73
- return {
74
- content: [{
75
- type: 'text',
76
- text: JSON.stringify(userStoryResults)
77
- }],
78
- };
79
- });
66
+ }, async ({ id }) => handleGetUserStoryContent(tp, id));
80
67
  server.registerTool('get_current_releases', {
81
68
  title: 'Get current releases',
82
69
  description: 'Get current releases',
83
- }, async ({}) => {
84
- const releases = await tp.getCurrentReleases();
85
- if (!releases) {
86
- return {
87
- content: [{
88
- type: 'text',
89
- text: `Failed to get current releases, JSON: ${JSON.stringify(releases, null, 2)}`
90
- }],
91
- };
92
- }
93
- const items = releases.Items || [];
94
- if (items.length == 0) {
95
- return {
96
- content: [{
97
- type: "text",
98
- text: `No releases found`,
99
- }],
100
- };
101
- }
102
- return {
103
- content: [{
104
- type: 'text',
105
- text: JSON.stringify(items)
106
- }],
107
- };
108
- });
70
+ }, async () => handleGetCurrentReleases(tp));
109
71
  server.registerTool('get_release_user_stories', {
110
72
  title: 'Get release user stories',
111
73
  description: 'Get release user stories',
@@ -117,32 +79,7 @@ server.registerTool('get_release_user_stories', {
117
79
  .optional()
118
80
  .describe('Number of results to return, default is 50'),
119
81
  },
120
- }, async ({ name, results }) => {
121
- const release = await tp.getReleaseUserStories({ name, results });
122
- if (!release) {
123
- return {
124
- content: [{
125
- type: 'text',
126
- text: `Failed to get ${name} release user stories, JSON: ${JSON.stringify(release, null, 2)}`
127
- }],
128
- };
129
- }
130
- const items = release.Items || [];
131
- if (items.length == 0) {
132
- return {
133
- content: [{
134
- type: "text",
135
- text: `No release user stories found`,
136
- }],
137
- };
138
- }
139
- return {
140
- content: [{
141
- type: 'text',
142
- text: JSON.stringify(items)
143
- }],
144
- };
145
- });
82
+ }, async ({ name, results }) => handleGetReleaseUserStories(tp, name, results));
146
83
  server.registerTool('get_release_bugs', {
147
84
  title: 'Get release bugs',
148
85
  description: 'Get release bugs',
@@ -154,32 +91,7 @@ server.registerTool('get_release_bugs', {
154
91
  .optional()
155
92
  .describe('Number of results to return, default is 100'),
156
93
  },
157
- }, async ({ name, results }) => {
158
- const release = await tp.getReleaseBugs({ name, results });
159
- if (!release) {
160
- return {
161
- content: [{
162
- type: 'text',
163
- text: `Failed to get ${name} release bugs, JSON: ${JSON.stringify(release, null, 2)}`
164
- }],
165
- };
166
- }
167
- const items = release.Items || [];
168
- if (items.length == 0) {
169
- return {
170
- content: [{
171
- type: "text",
172
- text: `No release bugs found`,
173
- }],
174
- };
175
- }
176
- return {
177
- content: [{
178
- type: 'text',
179
- text: JSON.stringify(items)
180
- }],
181
- };
182
- });
94
+ }, async ({ name, results }) => handleGetReleaseBugs(tp, name, results));
183
95
  server.registerTool('get_release_features', {
184
96
  title: 'Get release features',
185
97
  description: 'Get release features',
@@ -191,32 +103,7 @@ server.registerTool('get_release_features', {
191
103
  .optional()
192
104
  .describe('Number of results to return, default is 100'),
193
105
  },
194
- }, async ({ name, results }) => {
195
- const release = await tp.getReleaseFeatures({ name, results });
196
- if (!release) {
197
- return {
198
- content: [{
199
- type: 'text',
200
- text: `Failed to get ${name} release features, JSON: ${JSON.stringify(release, null, 2)}`
201
- }],
202
- };
203
- }
204
- const items = release.Items || [];
205
- if (items.length == 0) {
206
- return {
207
- content: [{
208
- type: "text",
209
- text: `No release features found`,
210
- }],
211
- };
212
- }
213
- return {
214
- content: [{
215
- type: 'text',
216
- text: JSON.stringify(items)
217
- }],
218
- };
219
- });
106
+ }, async ({ name, results }) => handleGetReleaseFeatures(tp, name, results));
220
107
  server.registerTool('get_release_user_stories_with_description', {
221
108
  title: 'Get release user stories with description',
222
109
  description: `Get release user stories with description in the response.
@@ -229,32 +116,7 @@ server.registerTool('get_release_user_stories_with_description', {
229
116
  withDescription: z.boolean()
230
117
  .describe('Include description in the response'),
231
118
  },
232
- }, async ({ name, withDescription }) => {
233
- const release = await tp.getReleaseUserStories({ name, withDescription });
234
- if (!release) {
235
- return {
236
- content: [{
237
- type: 'text',
238
- text: `Failed to get ${name} release user stories, JSON: ${JSON.stringify(release, null, 2)}`
239
- }],
240
- };
241
- }
242
- const items = release.Items || [];
243
- if (items.length == 0) {
244
- return {
245
- content: [{
246
- type: "text",
247
- text: `No release user stories found`,
248
- }],
249
- };
250
- }
251
- return {
252
- content: [{
253
- type: 'text',
254
- text: JSON.stringify(items)
255
- }],
256
- };
257
- });
119
+ }, async ({ name, withDescription }) => handleGetReleaseUserStories(tp, name, undefined, withDescription));
258
120
  server.registerTool('get_release_open_bugs', {
259
121
  title: 'Get release active bugs',
260
122
  description: `Get release active bugs (bugs that are not closed, done, passed, ready to deploy)`,
@@ -268,32 +130,7 @@ server.registerTool('get_release_open_bugs', {
268
130
  withDescription: z.boolean()
269
131
  .describe('Include description in the response'),
270
132
  },
271
- }, async ({ name, results, withDescription }) => {
272
- const release = await tp.getReleaseOpenBugs({ name, results, withDescription });
273
- if (!release) {
274
- return {
275
- content: [{
276
- type: 'text',
277
- text: `Failed to get ${name} release bugs, JSON: ${JSON.stringify(release, null, 2)}`
278
- }],
279
- };
280
- }
281
- const items = release.Items || [];
282
- if (items.length == 0) {
283
- return {
284
- content: [{
285
- type: "text",
286
- text: `No release bugs found`,
287
- }],
288
- };
289
- }
290
- return {
291
- content: [{
292
- type: 'text',
293
- text: JSON.stringify(items)
294
- }],
295
- };
296
- });
133
+ }, async ({ name, results, withDescription }) => handleGetReleaseOpenBugs(tp, name, results, withDescription));
297
134
  server.registerTool('get_release_open_user_stories', {
298
135
  title: 'Get release active user stories',
299
136
  description: `Get release active user stories (user stories that are not closed, done, passed, ready to deploy)`,
@@ -307,32 +144,7 @@ server.registerTool('get_release_open_user_stories', {
307
144
  withDescription: z.boolean()
308
145
  .describe('Include description in the response'),
309
146
  },
310
- }, async ({ name, results, withDescription }) => {
311
- const release = await tp.getReleaseOpenUserStories({ name, results, withDescription });
312
- if (!release) {
313
- return {
314
- content: [{
315
- type: 'text',
316
- text: `Failed to get ${name} release user stories, JSON: ${JSON.stringify(release, null, 2)}`
317
- }],
318
- };
319
- }
320
- const items = release.Items || [];
321
- if (items.length == 0) {
322
- return {
323
- content: [{
324
- type: "text",
325
- text: `No release user stories found`,
326
- }],
327
- };
328
- }
329
- return {
330
- content: [{
331
- type: 'text',
332
- text: JSON.stringify(items)
333
- }],
334
- };
335
- });
147
+ }, async ({ name, results, withDescription }) => handleGetReleaseOpenUserStories(tp, name, results, withDescription));
336
148
  server.registerTool('search_tp_cards', {
337
149
  title: 'Search TP cards by keyword or phrase in description',
338
150
  description: `Searches TP cards (UserStories or Bugs) by keyword or phrase or partial keyphrase in Card Description e.g. "Text Element", "Font field"
@@ -394,47 +206,7 @@ server.registerTool('get_bug_content', {
394
206
  .max(6)
395
207
  .describe('Bug card ID (e.g. 145789)')
396
208
  },
397
- }, async ({ id }) => {
398
- const bug = await tp.getBug(id);
399
- if (!bug) {
400
- return {
401
- content: [{
402
- type: 'text',
403
- text: `Failed to get bug, id: ${id}\n JSON: ${JSON.stringify(bug, null, 2)}`
404
- }],
405
- };
406
- }
407
- let bugResult = {
408
- name: bug.Name,
409
- id: bug.Id,
410
- description: '',
411
- origin: ''
412
- };
413
- try {
414
- const dom = new JSDOM(`<html><body><div id="content">${bug.Description}</div></body></html>`);
415
- const descriptionText = dom.window.document.getElementById('content')?.textContent;
416
- if (descriptionText) {
417
- bugResult.description = descriptionText;
418
- }
419
- }
420
- catch (error) {
421
- console.error("Error parsing bug description:", error);
422
- console.error("Returning bug without description");
423
- }
424
- try {
425
- bugResult.origin = bug.CustomFields?.find((field) => field?.Value === "Origin")?.Value;
426
- }
427
- catch (error) {
428
- console.error("Error parsing bug origin:", error);
429
- console.error("Returning bug without origin");
430
- }
431
- return {
432
- content: [{
433
- type: 'text',
434
- text: JSON.stringify(bugResult)
435
- }],
436
- };
437
- });
209
+ }, async ({ id }) => handleGetBugContent(tp, id));
438
210
  server.registerTool('get_user_by_id', {
439
211
  title: 'Get user by id',
440
212
  description: 'Get user by id',
@@ -442,52 +214,11 @@ server.registerTool('get_user_by_id', {
442
214
  id: z.string()
443
215
  .describe('User email'),
444
216
  },
445
- }, async ({ id }) => {
446
- const user = await tp.getUser(id);
447
- if (!user) {
448
- return {
449
- content: [{
450
- type: 'text',
451
- text: `Failed to get user, id: ${id}\n JSON: ${JSON.stringify(user, null, 2)}`
452
- }],
453
- };
454
- }
455
- return {
456
- content: [{
457
- type: 'text',
458
- text: JSON.stringify(user)
459
- }],
460
- };
461
- });
217
+ }, async ({ id }) => handleGetUserById(tp, id));
462
218
  server.registerTool('get_users', {
463
219
  title: 'Get users',
464
220
  description: 'Get all users',
465
- }, async () => {
466
- const response = await tp.getUsers();
467
- if (!response) {
468
- return {
469
- content: [{
470
- type: 'text',
471
- text: `Failed to get users, JSON: ${JSON.stringify(response, null, 2)}`
472
- }],
473
- };
474
- }
475
- const items = response.Items || [];
476
- if (items.length === 0) {
477
- return {
478
- content: [{
479
- type: 'text',
480
- text: `No users found`,
481
- }],
482
- };
483
- }
484
- return {
485
- content: [{
486
- type: 'text',
487
- text: JSON.stringify(items)
488
- }],
489
- };
490
- });
221
+ }, async () => handleGetUsers(tp));
491
222
  server.registerTool('add_comment_with_user', {
492
223
  title: 'Adds provided content to TP card (user story) as a comment',
493
224
  description: `Adds provided content as a comment to the specified tp card by id, e.g. 145789 and mentions the user in the comment
@@ -553,34 +284,7 @@ server.registerTool('add_comment', {
553
284
  comment: z.string()
554
285
  .describe('Comment content to add'),
555
286
  },
556
- }, async ({ id, comment }) => {
557
- try {
558
- const addCommentResponse = await tp.addComment(id, comment);
559
- if (!addCommentResponse) {
560
- return {
561
- content: [{
562
- type: 'text',
563
- text: `Failed to add comment to user story, id: ${id}\n JSON: ${JSON.stringify(addCommentResponse, null, 2)}`
564
- }]
565
- };
566
- }
567
- return {
568
- content: [{
569
- type: 'text',
570
- text: JSON.stringify(addCommentResponse)
571
- }],
572
- };
573
- }
574
- catch (error) {
575
- console.error("Error adding comment to user story:", error);
576
- return {
577
- content: [{
578
- type: 'text',
579
- text: `Failed to add comment to user story, id: ${id}\n Error: ${error}`
580
- }]
581
- };
582
- }
583
- });
287
+ }, async ({ id, comment }) => handleAddComment(tp, id, comment));
584
288
  server.registerTool('get_user_story_comments', {
585
289
  title: 'Get user story comments',
586
290
  description: 'Get comments for a TP user story by its ID',
@@ -594,54 +298,7 @@ server.registerTool('get_user_story_comments', {
594
298
  .optional()
595
299
  .describe('Number of comments to return, default is 25'),
596
300
  },
597
- }, async ({ id, results }) => {
598
- const response = await tp.getUserStoryComments(id, results);
599
- if (!response) {
600
- return {
601
- content: [{
602
- type: 'text',
603
- text: `Failed to get comments for user story id: ${id}`
604
- }],
605
- };
606
- }
607
- const items = response.Items || [];
608
- if (items.length === 0) {
609
- return {
610
- content: [{
611
- type: 'text',
612
- text: `No comments found for user story id: ${id}`,
613
- }],
614
- };
615
- }
616
- let parsedItems = [];
617
- try {
618
- parsedItems = items.map((item) => {
619
- const dom = new JSDOM(`<html><body><div id="content">${item.Description}</div></body></html>`);
620
- const descriptionText = dom.window.document.getElementById('content')?.textContent;
621
- return {
622
- id: item.Id,
623
- description: descriptionText,
624
- createDate: item.CreateDate,
625
- owner: item.Owner.FullName,
626
- };
627
- });
628
- }
629
- catch (error) {
630
- console.error("Error parsing user story comments:", error);
631
- return {
632
- content: [{
633
- type: 'text',
634
- text: `Failed to parse user story comments for user story id: ${id}`,
635
- }],
636
- };
637
- }
638
- return {
639
- content: [{
640
- type: 'text',
641
- text: JSON.stringify(parsedItems)
642
- }],
643
- };
644
- });
301
+ }, async ({ id, results }) => handleGetUserStoryComments(tp, id, results));
645
302
  server.registerTool('get_bug_comments', {
646
303
  title: 'Get bug comments',
647
304
  description: 'Get comments for a TP bug by its ID',
@@ -655,54 +312,7 @@ server.registerTool('get_bug_comments', {
655
312
  .optional()
656
313
  .describe('Number of comments to return, default is 25'),
657
314
  },
658
- }, async ({ id, results }) => {
659
- const response = await tp.getBugComments(id, results);
660
- if (!response) {
661
- return {
662
- content: [{
663
- type: 'text',
664
- text: `Failed to get comments for bug id: ${id}`
665
- }],
666
- };
667
- }
668
- const items = response.Items || [];
669
- if (items.length === 0) {
670
- return {
671
- content: [{
672
- type: 'text',
673
- text: `No comments found for bug id: ${id}`,
674
- }],
675
- };
676
- }
677
- let parsedItems = [];
678
- try {
679
- parsedItems = items.map((item) => {
680
- const dom = new JSDOM(`<html><body><div id="content">${item.Description}</div></body></html>`);
681
- const descriptionText = dom.window.document.getElementById('content')?.textContent;
682
- return {
683
- id: item.Id,
684
- description: descriptionText,
685
- createDate: item.CreateDate,
686
- owner: item.Owner.FullName,
687
- };
688
- });
689
- }
690
- catch (error) {
691
- console.error("Error parsing bug comments:", error);
692
- return {
693
- content: [{
694
- type: 'text',
695
- text: `Failed to parse bug comments for bug id: ${id}`,
696
- }],
697
- };
698
- }
699
- return {
700
- content: [{
701
- type: 'text',
702
- text: JSON.stringify(parsedItems)
703
- }],
704
- };
705
- });
315
+ }, async ({ id, results }) => handleGetBugComments(tp, id, results));
706
316
  server.registerTool('create_bug_based_on_card', {
707
317
  title: 'Create a new bug card based on provided card id',
708
318
  description: `Create a new bug card based on provided card id that summarizes the problem in concise, descriptive manner answering questions What? Where? When?, and content explaining what happened in detail.
@@ -802,31 +412,37 @@ server.registerTool('update_bug', {
802
412
  .describe('Where the bug was found, defaults to "Manual QA"'),
803
413
  projectId: z.string()
804
414
  .optional()
805
- .describe('Optional Project ID — if user gave a project name, resolve it via "get_projects" first; defaults to TP_PROJECT_ID from config'),
415
+ .describe('Optional Project ID — if user gave a project name, resolve it via "get_projects" first; defaults to TP_PROJECT_ID from config'),
416
+ teamId: z.string()
417
+ .optional()
418
+ .describe('Optional Team ID — if user gave a team name, resolve it via "get_teams" first; defaults to TP_TEAM_ID from config'),
419
+ entityStateId: z.string()
420
+ .optional()
421
+ .describe('Optional Entity State ID — if user gave a state name, resolve it via "get_bug_workflows" first; defaults to "Done"'),
422
+ },
423
+ }, async ({ id, title, bugContent, origin, projectId, teamId, entityStateId }) => handleUpdateBug(tp, { id, title, bugContent, origin, projectId, teamId, entityStateId }));
424
+ server.registerTool('update_user_story_state', {
425
+ title: 'Update a user story card sub state',
426
+ description: `Update a user story card sub state with data provided from user input.
427
+ CRITICAL WORKFLOW: Before calling this tool, you MUST follow these steps:
428
+ 1) call "get_user_story_content" to find the matching team, assigned (responsible) team and their IDs
429
+ 1) call "get_user_story_workflows" to find matching state and use its ID in entityStateId`,
430
+ inputSchema: {
431
+ id: z.string()
432
+ .min(5)
433
+ .max(6)
434
+ .describe('User story card ID (e.g. 145789)'),
435
+ entityStateId: z.string()
436
+ .optional()
437
+ .describe('Entity state ID, resolve it via "get_user_story_workflows" first'),
806
438
  teamId: z.string()
807
439
  .optional()
808
- .describe('Optional Team ID — if user gave a team name, resolve it via "get_teams" first; defaults to TP_TEAM_ID from config'),
809
- entityStateId: z.string()
440
+ .describe('Team ID, resolve it via "get_teams" first'),
441
+ teamAssignmentId: z.string()
810
442
  .optional()
811
- .describe('Optional Entity State ID — if user gave a state name, resolve it via "get_bug_workflows" first; defaults to "Done"'),
443
+ .describe('Team Assignment ID, resolve it via "get_user_story_content" first'),
812
444
  },
813
- }, async ({ id, title, bugContent, origin, projectId, teamId, entityStateId }) => {
814
- const bugResponse = await tp.updateBug({ id, title, bugContent, origin, projectId, teamId, entityStateId });
815
- if (!bugResponse) {
816
- return {
817
- content: [{
818
- type: 'text',
819
- text: `Failed to update bug "${title}"\n JSON: ${JSON.stringify(bugResponse, null, 2)}`
820
- }]
821
- };
822
- }
823
- return {
824
- content: [{
825
- type: 'text',
826
- text: JSON.stringify(bugResponse)
827
- }],
828
- };
829
- });
445
+ }, async ({ id, teamId, teamAssignmentId, entityStateId }) => handleUpdateUserStorySubState(tp, { id, teamId, teamAssignmentId, entityStateId }));
830
446
  server.registerTool('update_user_story', {
831
447
  title: 'Update a user story card',
832
448
  description: `Update a user story card with data provided from user input.
@@ -910,23 +526,7 @@ server.registerTool('create_bug', {
910
526
  .optional()
911
527
  .describe('Optional Entity State ID — if user gave a state name, resolve it via "get_bug_workflows" first; defaults to "Done"'),
912
528
  },
913
- }, async ({ title, bugContent, origin, projectId, teamId, entityStateId }) => {
914
- const bugResponse = await tp.createBugOnly({ title, bugContent, origin, projectId, teamId, entityStateId });
915
- if (!bugResponse) {
916
- return {
917
- content: [{
918
- type: 'text',
919
- text: `Failed to create bug "${title}"\n JSON: ${JSON.stringify(bugResponse, null, 2)}`
920
- }]
921
- };
922
- }
923
- return {
924
- content: [{
925
- type: 'text',
926
- text: JSON.stringify(bugResponse)
927
- }],
928
- };
929
- });
529
+ }, async ({ title, bugContent, origin, projectId, teamId, entityStateId }) => handleCreateBug(tp, { title, bugContent, origin, projectId, teamId, entityStateId }));
930
530
  server.registerTool('create_user_story', {
931
531
  title: 'Create a new user story',
932
532
  description: `Create a new user story in Targetprocess.`,
@@ -953,23 +553,7 @@ server.registerTool('create_user_story', {
953
553
  .optional()
954
554
  .describe('Optional Team ID — defaults to TP_TEAM_ID from config'),
955
555
  },
956
- }, async ({ title, description, featureId, releaseId, projectId, teamId }) => {
957
- const userStoryResponse = await tp.createUserStory({ title, description, featureId, releaseId, projectId, teamId });
958
- if (!userStoryResponse) {
959
- return {
960
- content: [{
961
- type: 'text',
962
- text: `Failed to create user story "${title}"\n JSON: ${JSON.stringify(userStoryResponse, null, 2)}`
963
- }]
964
- };
965
- }
966
- return {
967
- content: [{
968
- type: 'text',
969
- text: JSON.stringify(userStoryResponse)
970
- }],
971
- };
972
- });
556
+ }, async ({ title, description, featureId, releaseId, projectId, teamId }) => handleCreateUserStory(tp, { title, description, featureId, releaseId, projectId, teamId }));
973
557
  server.registerTool('create_feature', {
974
558
  title: 'Create a new feature',
975
559
  description: `Create a new Feature in Targetprocess.`,
@@ -996,23 +580,7 @@ server.registerTool('create_feature', {
996
580
  .optional()
997
581
  .describe('Optional Team ID — defaults to TP_TEAM_ID from config'),
998
582
  },
999
- }, async ({ title, description, epicId, releaseId, projectId, teamId }) => {
1000
- const featureResponse = await tp.createFeature({ title, description, epicId, releaseId, projectId, teamId });
1001
- if (!featureResponse) {
1002
- return {
1003
- content: [{
1004
- type: 'text',
1005
- text: `Failed to create feature "${title}"\n JSON: ${JSON.stringify(featureResponse, null, 2)}`
1006
- }]
1007
- };
1008
- }
1009
- return {
1010
- content: [{
1011
- type: 'text',
1012
- text: JSON.stringify(featureResponse)
1013
- }],
1014
- };
1015
- });
583
+ }, async ({ title, description, epicId, releaseId, projectId, teamId }) => handleCreateFeature(tp, { title, description, epicId, releaseId, projectId, teamId }));
1016
584
  server.registerTool('create_test_plan', {
1017
585
  title: 'Create a new test plan linked to a TP card',
1018
586
  description: `Create a new test plan linked to a UserStory, Bug, or Feature. Name and Project are required by the API; Description, StartDate, and EndDate are optional.`,
@@ -1146,32 +714,7 @@ server.registerTool('get_feature_user_stories', {
1146
714
  .max(6)
1147
715
  .describe('TP feature ID (e.g. 145636)'),
1148
716
  },
1149
- }, async ({ id }) => {
1150
- const response = await tp.getFeatureUserStories(id);
1151
- if (!response) {
1152
- return {
1153
- content: [{
1154
- type: 'text',
1155
- text: `Failed to get user stories for feature id: ${id}`
1156
- }],
1157
- };
1158
- }
1159
- const items = response.items || [];
1160
- if (items.length === 0) {
1161
- return {
1162
- content: [{
1163
- type: 'text',
1164
- text: `No user stories found in outer items for feature id: ${id}`,
1165
- }],
1166
- };
1167
- }
1168
- return {
1169
- content: [{
1170
- type: 'text',
1171
- text: JSON.stringify(items)
1172
- }],
1173
- };
1174
- });
717
+ }, async ({ id }) => handleGetFeatureUserStories(tp, id));
1175
718
  server.registerTool('get_user_story_bugs', {
1176
719
  title: 'Get user story bugs',
1177
720
  description: 'Get bugs linked to a TP user story by its ID',
@@ -1181,119 +724,23 @@ server.registerTool('get_user_story_bugs', {
1181
724
  .max(6)
1182
725
  .describe('TP user story ID (e.g. 145789)'),
1183
726
  },
1184
- }, async ({ id }) => {
1185
- const response = await tp.getUserStoryBugs(id);
1186
- if (!response) {
1187
- return {
1188
- content: [{
1189
- type: 'text',
1190
- text: `Failed to get bugs for user story id: ${id}`
1191
- }],
1192
- };
1193
- }
1194
- const items = response.items || [];
1195
- if (items.length === 0) {
1196
- return {
1197
- content: [{
1198
- type: 'text',
1199
- text: `No bugs found for user story id: ${id}`,
1200
- }],
1201
- };
1202
- }
1203
- return {
1204
- content: [{
1205
- type: 'text',
1206
- text: JSON.stringify(items)
1207
- }],
1208
- };
1209
- });
727
+ }, async ({ id }) => handleGetUserStoryBugs(tp, id));
1210
728
  server.registerTool('get_projects', {
1211
729
  title: 'Get projects',
1212
730
  description: 'Get all Targetprocess projects',
1213
- }, async ({}) => {
1214
- const response = await tp.getProjects();
1215
- if (!response) {
1216
- return {
1217
- content: [{
1218
- type: 'text',
1219
- text: `Failed to get projects, JSON: ${JSON.stringify(response, null, 2)}`
1220
- }],
1221
- };
1222
- }
1223
- const items = response.Items || [];
1224
- if (items.length === 0) {
1225
- return {
1226
- content: [{
1227
- type: 'text',
1228
- text: `No projects found`,
1229
- }],
1230
- };
1231
- }
1232
- return {
1233
- content: [{
1234
- type: 'text',
1235
- text: JSON.stringify(items.map((p) => ({ id: p.Id, name: p.Name })))
1236
- }],
1237
- };
1238
- });
731
+ }, async () => handleGetProjects(tp));
732
+ server.registerTool('get_teams_and_team_assignments', {
733
+ title: 'Get teams and team assignments',
734
+ description: 'Get all Targetprocess teams and team assignments',
735
+ }, async () => handleGetTeamsAndTeamAssignments(tp));
1239
736
  server.registerTool('get_teams', {
1240
737
  title: 'Get teams',
1241
738
  description: 'Get all Targetprocess teams',
1242
- }, async ({}) => {
1243
- const response = await tp.getTeams();
1244
- if (!response) {
1245
- return {
1246
- content: [{
1247
- type: 'text',
1248
- text: `Failed to get teams, JSON: ${JSON.stringify(response, null, 2)}`
1249
- }],
1250
- };
1251
- }
1252
- const items = response.Items || [];
1253
- if (items.length === 0) {
1254
- return {
1255
- content: [{
1256
- type: 'text',
1257
- text: `No teams found`,
1258
- }],
1259
- };
1260
- }
1261
- return {
1262
- content: [{
1263
- type: 'text',
1264
- text: JSON.stringify(items.map((t) => ({ id: t.Id, name: t.Name })))
1265
- }],
1266
- };
1267
- });
739
+ }, async () => handleGetTeams(tp));
1268
740
  server.registerTool('get_logged_in_user', {
1269
741
  title: 'Get logged in user',
1270
742
  description: 'Get logged in user',
1271
- }, async () => {
1272
- const ctx = await tp.getContext();
1273
- if (!ctx) {
1274
- return {
1275
- content: [{
1276
- type: 'text',
1277
- text: `Failed to get context, JSON: ${JSON.stringify(ctx, null, 2)}`
1278
- }],
1279
- };
1280
- }
1281
- const loggedInUser = ctx.LoggedUser;
1282
- if (!loggedInUser) {
1283
- return {
1284
- content: [{
1285
- type: 'text',
1286
- text: `Failed to get logged in user in this context, JSON: ${JSON.stringify(ctx, null, 2)}`
1287
- }],
1288
- };
1289
- }
1290
- return {
1291
- content: [{
1292
- type: 'text',
1293
- text: JSON.stringify(loggedInUser)
1294
- }],
1295
- };
1296
- });
743
+ }, async () => handleGetLoggedInUser(tp));
1297
744
  server.registerTool('get_user_story_test_cases', {
1298
745
  title: 'Get test cases for TP UserStory card',
1299
746
  description: `Fetches a TP UserStory Linked Test Plan and fetches its Test Cases by provided card ID.`,
@@ -1613,8 +1060,7 @@ server.registerTool('get_user_story_workflows', {
1613
1060
  id: w.id,
1614
1061
  processId: w.workflow.process.id,
1615
1062
  entityType: w.entityType.name,
1616
- entityState: w.name,
1617
- entitySubStates: w.subEntityStates.map((es) => ({
1063
+ entityStates: w.subEntityStates.map((es) => ({
1618
1064
  id: es.id,
1619
1065
  name: es.name,
1620
1066
  })),
@@ -1639,32 +1085,7 @@ server.registerTool('get_card_current_status', {
1639
1085
  .optional()
1640
1086
  .describe('Type of the TP card — UserStory, Bug, or Feature (default: UserStory)'),
1641
1087
  },
1642
- }, async ({ id, resourceType = 'UserStory' }) => {
1643
- const response = await tp.getCardStatus(id, resourceType);
1644
- if (!response) {
1645
- return {
1646
- content: [{
1647
- type: 'text',
1648
- text: `Failed to get card status for ${resourceType} id: ${id}`
1649
- }],
1650
- };
1651
- }
1652
- const items = response.items || [];
1653
- if (items.length === 0) {
1654
- return {
1655
- content: [{
1656
- type: 'text',
1657
- text: `No status data found for ${resourceType} id: ${id}`,
1658
- }],
1659
- };
1660
- }
1661
- return {
1662
- content: [{
1663
- type: 'text',
1664
- text: JSON.stringify(items[0])
1665
- }],
1666
- };
1667
- });
1088
+ }, async ({ id, resourceType = 'UserStory' }) => handleGetCardCurrentStatus(tp, id, resourceType));
1668
1089
  server.registerTool('get_in_progress_tasks_and_bugs', {
1669
1090
  title: 'Get in-progress tasks and bugs for a user',
1670
1091
  description: 'Get all Tasks and Bugs currently in "In Progress" state assigned to a given user ID',
@@ -1672,44 +1093,7 @@ server.registerTool('get_in_progress_tasks_and_bugs', {
1672
1093
  userId: z.string()
1673
1094
  .describe('Targetprocess user ID (e.g. 123)'),
1674
1095
  },
1675
- }, async ({ userId }) => {
1676
- const result = await tp.getInProgressTasksAndBugs(userId);
1677
- const tasks = result.tasks.map((t) => ({
1678
- type: 'Task',
1679
- id: t.Id,
1680
- name: t.Name,
1681
- state: t.EntityState?.Name,
1682
- userStoryId: t.UserStory?.Id,
1683
- userStoryName: t.UserStory?.Name,
1684
- featureId: t.UserStory?.Feature?.Id,
1685
- featureName: t.UserStory?.Feature?.Name,
1686
- }));
1687
- const bugs = result.bugs.map((b) => ({
1688
- type: 'Bug',
1689
- id: b.Id,
1690
- name: b.Name,
1691
- state: b.EntityState?.Name,
1692
- userStoryId: b.UserStory?.Id,
1693
- userStoryName: b.UserStory?.Name,
1694
- featureId: b.UserStory?.Feature?.Id ?? b.Feature?.Id,
1695
- featureName: b.UserStory?.Feature?.Name ?? b.Feature?.Name,
1696
- }));
1697
- const items = [...tasks, ...bugs];
1698
- if (items.length === 0) {
1699
- return {
1700
- content: [{
1701
- type: 'text',
1702
- text: `No in-progress tasks or bugs found for user ID: ${userId}`,
1703
- }],
1704
- };
1705
- }
1706
- return {
1707
- content: [{
1708
- type: 'text',
1709
- text: JSON.stringify(items),
1710
- }],
1711
- };
1712
- });
1096
+ }, async ({ userId }) => handleGetInProgressTasksAndBugs(tp, userId));
1713
1097
  server.registerTool('create_task', {
1714
1098
  title: 'Create a new task',
1715
1099
  description: 'Create a new task linked to a user story.',
@@ -1724,23 +1108,7 @@ server.registerTool('create_task', {
1724
1108
  .optional()
1725
1109
  .describe('Task description (optional)'),
1726
1110
  },
1727
- }, async ({ title, userStoryId, description }) => {
1728
- const taskResponse = await tp.createTask({ title, userStoryId, description });
1729
- if (!taskResponse) {
1730
- return {
1731
- content: [{
1732
- type: 'text',
1733
- text: `Failed to create task "${title}"\n JSON: ${JSON.stringify(taskResponse, null, 2)}`
1734
- }]
1735
- };
1736
- }
1737
- return {
1738
- content: [{
1739
- type: 'text',
1740
- text: JSON.stringify(taskResponse)
1741
- }],
1742
- };
1743
- });
1111
+ }, async ({ title, userStoryId, description }) => handleCreateTask(tp, { title, userStoryId, description }));
1744
1112
  server.registerTool('get_commit_message', {
1745
1113
  title: 'Get commit message for a task or bug',
1746
1114
  description: `Returns the formatted commit message string for a given task or bug ID.
@@ -1754,49 +1122,71 @@ Formats:
1754
1122
  type: z.enum(['task', 'bug'])
1755
1123
  .describe('Whether the ID refers to a task or a bug'),
1756
1124
  },
1757
- }, async ({ id, type }) => {
1758
- if (type === 'task') {
1759
- const task = await tp.getTask(id);
1760
- if (!task) {
1761
- return {
1762
- content: [{ type: 'text', text: `Failed to get task with id: ${id}` }],
1763
- };
1764
- }
1765
- const userStory = task.UserStory;
1766
- const feature = userStory?.Feature;
1767
- if (!userStory) {
1768
- return {
1769
- content: [{ type: 'text', text: `Task ${id} has no linked user story` }],
1770
- };
1771
- }
1772
- const prefix = feature
1773
- ? `F#${feature.Id} US#${userStory.Id} T#${task.Id}`
1774
- : `US#${userStory.Id} T#${task.Id}`;
1775
- return {
1776
- content: [{ type: 'text', text: `${prefix} ${task.Name}` }],
1777
- };
1778
- }
1779
- // type === 'bug'
1780
- const bug = await tp.getBugWithRelations(id);
1781
- if (!bug) {
1782
- return {
1783
- content: [{ type: 'text', text: `Failed to get bug with id: ${id}` }],
1784
- };
1785
- }
1786
- const userStory = bug.UserStory;
1787
- const feature = userStory?.Feature ?? bug.Feature;
1788
- if (!userStory) {
1789
- return {
1790
- content: [{ type: 'text', text: `B#${bug.Id} ${bug.Name}` }],
1791
- };
1792
- }
1793
- const prefix = feature
1794
- ? `F#${feature.Id} US#${userStory.Id} B#${bug.Id}`
1795
- : `US#${userStory.Id} B#${bug.Id}`;
1796
- return {
1797
- content: [{ type: 'text', text: `${prefix} ${bug.Name}` }],
1798
- };
1799
- });
1125
+ }, async ({ id, type }) => handleGetCommitMessage(tp, id, type));
1126
+ server.registerTool('list_my_user_stories', {
1127
+ title: 'List my user stories',
1128
+ description: 'List User Stories assigned to me. Use this to get an overview of current work. Optionally filter by state.',
1129
+ inputSchema: {
1130
+ state: z.string()
1131
+ .optional()
1132
+ .describe('Filter by state name (e.g. "Open", "In Progress", "Done")'),
1133
+ take: z.number()
1134
+ .default(25)
1135
+ .optional()
1136
+ .describe('Number of results to return, default is 25'),
1137
+ skip: z.number()
1138
+ .default(0)
1139
+ .optional()
1140
+ .describe('Pagination offset, default is 0'),
1141
+ },
1142
+ }, async ({ state, take, skip }) => handleListMyUserStories(tp, { state, take, skip }));
1143
+ server.registerTool('list_my_bugs', {
1144
+ title: 'List my bugs',
1145
+ description: 'List Bugs assigned to me. Optionally filter by state.',
1146
+ inputSchema: {
1147
+ state: z.string()
1148
+ .optional()
1149
+ .describe('Filter by state name (e.g. "Open", "In Progress", "Fixed")'),
1150
+ take: z.number()
1151
+ .default(25)
1152
+ .optional()
1153
+ .describe('Number of results to return, default is 25'),
1154
+ skip: z.number()
1155
+ .default(0)
1156
+ .optional()
1157
+ .describe('Pagination offset, default is 0'),
1158
+ },
1159
+ }, async ({ state, take, skip }) => handleListMyBugs(tp, { state, take, skip }));
1160
+ server.registerTool('log_time', {
1161
+ title: 'Log time on a Task, User Story, or Bug',
1162
+ description: 'Log time spent working on a Task, User Story, or Bug. Call this after completing a task or at the end of a work session.',
1163
+ inputSchema: {
1164
+ entityId: z.string()
1165
+ .min(1)
1166
+ .describe('ID of the Task, User Story, or Bug to log time against (e.g. 145789)'),
1167
+ entityType: z.enum(['Task', 'UserStory', 'Bug'])
1168
+ .describe('Type of the entity'),
1169
+ hours: z.number()
1170
+ .positive()
1171
+ .describe('Hours spent (can be decimal e.g. 1.5)'),
1172
+ description: z.string()
1173
+ .optional()
1174
+ .describe('What was done — brief summary of the work'),
1175
+ date: z.string()
1176
+ .optional()
1177
+ .describe('ISO date string, defaults to today (e.g. "2024-05-21")'),
1178
+ },
1179
+ }, async ({ entityId, entityType, hours, description, date }) => handleLogTime(tp, { entityId, entityType, hours, description, date }));
1180
+ server.registerTool('get_my_time_logs', {
1181
+ title: 'Get my recent time log entries',
1182
+ description: 'Get recent time log entries submitted by me.',
1183
+ inputSchema: {
1184
+ take: z.number()
1185
+ .default(25)
1186
+ .optional()
1187
+ .describe('Number of entries to return, default is 25'),
1188
+ },
1189
+ }, async ({ take }) => handleGetMyTimeLogs(tp, take));
1800
1190
  async function main() {
1801
1191
  const transport = new StdioServerTransport();
1802
1192
  await server.connect(transport);