targetprocess-mcp-server 2.2.3-a → 2.2.4

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 CHANGED
@@ -46,6 +46,7 @@ Features
46
46
  - `get_not_covered_user_stories_in_feature` — Get user stories in a feature not yet covered by tests, includes `covered` field based on "Test Automation" custom field (id)
47
47
 
48
48
  Cards — Read
49
+ - `get_card_status` — Get EntityState, TeamState, and assigned teams for a card (id, optional resourceType: UserStory | Bug | Feature, default: UserStory)
49
50
  - `get_bug_content` — Fetch full content of a bug by ID (id)
50
51
  - `get_user_story_content` — Fetch full content of a user story by ID (id)
51
52
  - `get_bug_comments` — Get comments on a bug (id, optional results)
package/build/index.js CHANGED
@@ -600,11 +600,13 @@ server.registerTool('create_bug_based_on_card', {
600
600
  title: 'Create a new bug card based on provided card id',
601
601
  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.
602
602
  NOTE: this tool requires a user story or bug card as a reference (i.e. card ID).
603
- CRITICAL WORKFLOW: Before calling this tool, you MUST follow these steps:
603
+ CRITICAL WORKFLOW: Before calling this tool, you MUST follow these steps:
604
604
  1) IF you already have user story or bug card content, proceed to step 3 skipping step 2;
605
605
  2) ELSE call "get_user_story_content" tool or "get_bug_content" tool to get user story or bug card content;
606
606
  3) format the new bug inside html <div> tags with Issue Description, Steps to Reproduce, Expected Behavior, Actual Behavior sections (note: section titles should be wrapped in <h3> tags, e.g. <h3>Issue Description</h3>);
607
- 4) add a comment to the card with created bug Id and its Title`,
607
+ 4) IF the user specified a team by name (not ID), call "get_teams" to find the matching team and use its ID as teamId;
608
+ 5) IF the user specified a project by name (not ID), call "get_projects" to find the matching project and use its ID as projectId;
609
+ 6) add a comment to the card with created bug Id and its Title`,
608
610
  inputSchema: {
609
611
  title: z.string()
610
612
  .describe('Bug card title that summarizes the problem in concise, descriptive, and actionable manner, enabling a developer to understand the issue without opening the report'),
@@ -637,10 +639,10 @@ server.registerTool('create_bug_based_on_card', {
637
639
  .describe('Where the bug was found, defaults to "Manual QA"'),
638
640
  projectId: z.string()
639
641
  .optional()
640
- .describe('Optional Project ID — defaults to TP_PROJECT_ID from config'),
642
+ .describe('Optional Project ID — if user gave a project name, resolve it via "get_projects" first; defaults to TP_PROJECT_ID from config'),
641
643
  teamId: z.string()
642
644
  .optional()
643
- .describe('Optional Team ID — defaults to TP_TEAM_ID from config'),
645
+ .describe('Optional Team ID — if user gave a team name, resolve it via "get_teams" first; defaults to TP_TEAM_ID from config'),
644
646
  },
645
647
  }, async ({ title, card, bugContent, origin, projectId, teamId }) => {
646
648
  const bugResponse = await tp.createBug({ title, card, bugContent, origin, projectId, teamId });
@@ -664,7 +666,9 @@ server.registerTool('create_bug', {
664
666
  description: `Create a new bug card that summarizes the problem in concise, descriptive manner answering questions "What? Where? When?" and content explaining what happened in detail.
665
667
  NOTE: this tool does not require a user story or bug card reference.
666
668
  CRITICAL WORKFLOW: Before calling this tool, you MUST follow these steps:
667
- 1) format the new bug inside html <div> tags with Issue Description, Steps to Reproduce, Expected Behavior, Actual Behavior sections (note: section titles should be wrapped in <h3> tags, e.g. <h3>Issue Description</h3>);`,
669
+ 1) format the new bug inside html <div> tags with Issue Description, Steps to Reproduce, Expected Behavior, Actual Behavior sections (note: section titles should be wrapped in <h3> tags, e.g. <h3>Issue Description</h3>, step to reproduce should be wrapped in <ol>);
670
+ 2) IF the user specified a team by name (not ID), call "get_teams" to find the matching team and use its ID as teamId;
671
+ 3) IF the user specified a project by name (not ID), call "get_projects" to find the matching project and use its ID as projectId;`,
668
672
  inputSchema: {
669
673
  title: z.string()
670
674
  .describe('Bug card title that summarizes the problem in concise, descriptive, and actionable manner, enabling a developer to understand the issue without opening the report'),
@@ -686,10 +690,10 @@ server.registerTool('create_bug', {
686
690
  .describe('Where the bug was found, defaults to "Manual QA"'),
687
691
  projectId: z.string()
688
692
  .optional()
689
- .describe('Optional Project ID — defaults to TP_PROJECT_ID from config'),
693
+ .describe('Optional Project ID — if user gave a project name, resolve it via "get_projects" first; defaults to TP_PROJECT_ID from config'),
690
694
  teamId: z.string()
691
695
  .optional()
692
- .describe('Optional Team ID — defaults to TP_TEAM_ID from config'),
696
+ .describe('Optional Team ID — if user gave a team name, resolve it via "get_teams" first; defaults to TP_TEAM_ID from config'),
693
697
  },
694
698
  }, async ({ title, bugContent, origin, projectId, teamId }) => {
695
699
  const bugResponse = await tp.createBugOnly({ title, bugContent, origin, projectId, teamId });
@@ -1240,6 +1244,45 @@ server.registerTool('add_test_cases_to_test_plan', {
1240
1244
  }]
1241
1245
  };
1242
1246
  });
1247
+ server.registerTool('get_card_status', {
1248
+ title: 'Get card status',
1249
+ description: 'Get the EntityState, TeamState, and assigned teams for a TP card (UserStory, Bug, or Feature) by ID',
1250
+ inputSchema: {
1251
+ id: z.string()
1252
+ .min(5)
1253
+ .max(6)
1254
+ .describe('TP card ID (e.g. 146055)'),
1255
+ resourceType: z.enum(['UserStory', 'Bug', 'Feature'])
1256
+ .default('UserStory')
1257
+ .optional()
1258
+ .describe('Type of the TP card — UserStory, Bug, or Feature (default: UserStory)'),
1259
+ },
1260
+ }, async ({ id, resourceType = 'UserStory' }) => {
1261
+ const response = await tp.getCardStatus(id, resourceType);
1262
+ if (!response) {
1263
+ return {
1264
+ content: [{
1265
+ type: 'text',
1266
+ text: `Failed to get card status for ${resourceType} id: ${id}`
1267
+ }],
1268
+ };
1269
+ }
1270
+ const items = response.items || [];
1271
+ if (items.length === 0) {
1272
+ return {
1273
+ content: [{
1274
+ type: 'text',
1275
+ text: `No status data found for ${resourceType} id: ${id}`,
1276
+ }],
1277
+ };
1278
+ }
1279
+ return {
1280
+ content: [{
1281
+ type: 'text',
1282
+ text: JSON.stringify(items[0])
1283
+ }],
1284
+ };
1285
+ });
1243
1286
  async function main() {
1244
1287
  const transport = new StdioServerTransport();
1245
1288
  await server.connect(transport);
package/build/tp.js CHANGED
@@ -15,8 +15,8 @@ export class TpClient {
15
15
  }
16
16
  params(params) {
17
17
  let _url = this.baseUrl + (params.apiVersion || this.v1);
18
- for (const [key, value] of Object.entries(params.pathParam)) {
19
- _url += value ? `/${key}/${value}` : `/${key}`;
18
+ for (const segment of params.pathParam) {
19
+ _url += `/${segment}`;
20
20
  }
21
21
  let _urlParams = [];
22
22
  for (const [key, value] of Object.entries(params.param)) {
@@ -83,21 +83,21 @@ export class TpClient {
83
83
  }
84
84
  async getUserStory(userStoryId) {
85
85
  const response = await this.get({
86
- pathParam: { "userStories": userStoryId },
86
+ pathParam: ["userStories", userStoryId],
87
87
  param: { "format": "json" },
88
88
  });
89
89
  return response;
90
90
  }
91
91
  async getBug(bugId) {
92
92
  const response = await this.get({
93
- pathParam: { "bugs": bugId },
93
+ pathParam: ["bugs", bugId],
94
94
  param: { "format": "json" }
95
95
  });
96
96
  return response;
97
97
  }
98
98
  async getFeature(featureId) {
99
99
  const response = await this.get({
100
- pathParam: { "features": featureId },
100
+ pathParam: ["features", featureId],
101
101
  param: { "format": "json" }
102
102
  });
103
103
  return response;
@@ -126,7 +126,7 @@ export class TpClient {
126
126
  };
127
127
  }
128
128
  return this.post({
129
- pathParam: { "bugs": '' },
129
+ pathParam: ["bugs"],
130
130
  param: { "format": "json" },
131
131
  }, bug);
132
132
  }
@@ -149,7 +149,7 @@ export class TpClient {
149
149
  "Description": bugContent,
150
150
  };
151
151
  return this.post({
152
- pathParam: { "bugs": '' },
152
+ pathParam: ["bugs"],
153
153
  param: { "format": "json" },
154
154
  }, bug);
155
155
  }
@@ -166,7 +166,7 @@ export class TpClient {
166
166
  if (releaseId)
167
167
  userStory["Release"] = { "Id": releaseId };
168
168
  return this.post({
169
- pathParam: { "UserStories": '' },
169
+ pathParam: ["UserStories"],
170
170
  param: { "format": "json" },
171
171
  }, userStory);
172
172
  }
@@ -183,7 +183,7 @@ export class TpClient {
183
183
  if (releaseId)
184
184
  feature["Release"] = { "Id": releaseId };
185
185
  return this.post({
186
- pathParam: { "Features": '' },
186
+ pathParam: ["Features"],
187
187
  param: { "format": "json" },
188
188
  }, feature);
189
189
  }
@@ -209,7 +209,7 @@ export class TpClient {
209
209
  "Description": bugContent,
210
210
  };
211
211
  return this.post({
212
- pathParam: { "bugs": '' },
212
+ pathParam: ["bugs"],
213
213
  param: { "format": "json" },
214
214
  }, bug);
215
215
  }
@@ -223,7 +223,7 @@ export class TpClient {
223
223
  }],
224
224
  };
225
225
  return this.post({
226
- pathParam: { "testCases": '' },
226
+ pathParam: ["testCases"],
227
227
  param: { "format": "json" },
228
228
  }, testCase);
229
229
  }
@@ -260,7 +260,7 @@ export class TpClient {
260
260
  if (options?.endDate)
261
261
  testPlan["EndDate"] = options.endDate;
262
262
  return this.post({
263
- pathParam: { "testPlans": '' },
263
+ pathParam: ["testPlans"],
264
264
  param: { "format": "json" },
265
265
  }, testPlan);
266
266
  }
@@ -275,7 +275,7 @@ export class TpClient {
275
275
  },
276
276
  };
277
277
  return this.post({
278
- pathParam: { "comments": '' },
278
+ pathParam: ["comments"],
279
279
  param: { "format": "json" },
280
280
  }, commentData);
281
281
  }
@@ -286,16 +286,13 @@ export class TpClient {
286
286
  "TestCase": { "Id": testCaseId },
287
287
  };
288
288
  return this.post({
289
- pathParam: { "testSteps": '' },
289
+ pathParam: ["testSteps"],
290
290
  param: { "format": "json" },
291
291
  }, testStepData);
292
292
  }
293
293
  async getBugComments(bugId, results = 25) {
294
294
  const response = await this.get({
295
- pathParam: {
296
- "Bugs": bugId,
297
- "Comments": "",
298
- },
295
+ pathParam: ["Bugs", bugId, "Comments"],
299
296
  param: {
300
297
  "format": "json",
301
298
  "take": results,
@@ -305,10 +302,7 @@ export class TpClient {
305
302
  }
306
303
  async getUserStoryComments(userStoryId, results = 25) {
307
304
  const response = await this.get({
308
- pathParam: {
309
- "UserStories": userStoryId,
310
- "Comments": "",
311
- },
305
+ pathParam: ["UserStories", userStoryId, "Comments"],
312
306
  param: {
313
307
  "format": "json",
314
308
  "take": results,
@@ -318,7 +312,7 @@ export class TpClient {
318
312
  }
319
313
  async searchContainsNameText({ text, entityType }) {
320
314
  return this.get({
321
- pathParam: { [entityType]: '' },
315
+ pathParam: [entityType],
322
316
  param: {
323
317
  "format": "json",
324
318
  "take": "25",
@@ -329,7 +323,7 @@ export class TpClient {
329
323
  }
330
324
  async searchContainsDescriptionText({ text, entityType }) {
331
325
  return this.get({
332
- pathParam: { [entityType]: '' },
326
+ pathParam: [entityType],
333
327
  param: {
334
328
  "where": `Description contains '${text}' and EntityState.Name eq 'Done'`,
335
329
  "format": "json",
@@ -339,7 +333,7 @@ export class TpClient {
339
333
  }
340
334
  async getCurrentReleases() {
341
335
  return this.get({
342
- pathParam: { "Releases": '' },
336
+ pathParam: ["Releases"],
343
337
  param: {
344
338
  "format": "json",
345
339
  "where": `IsCurrent eq 'true'`,
@@ -349,7 +343,7 @@ export class TpClient {
349
343
  async getReleaseUserStories({ name, results = 50, withDescription = false }) {
350
344
  const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
351
345
  return this.get({
352
- pathParam: { "UserStories": '' },
346
+ pathParam: ["UserStories"],
353
347
  param: {
354
348
  "format": "json",
355
349
  "take": results,
@@ -361,11 +355,11 @@ export class TpClient {
361
355
  async getReleaseOpenUserStories({ name, results = 100, withDescription = false }) {
362
356
  const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
363
357
  return this.get({
364
- pathParam: { "UserStories": '' },
358
+ pathParam: ["UserStories"],
365
359
  param: {
366
360
  "format": "json",
367
361
  "take": results,
368
- "where": `Release.Name eq '${name}' and EntityState.Name ne 'Closed' and EntityState.Name ne 'Done' and EntityState.Name ne 'Passed Dev01  QA' and EntityState.Name ne 'Ready to Deploy to prod'`,
362
+ "where": `Release.Name eq '${name}' and EntityState.Name ne 'Closed' and EntityState.Name ne 'Done' and EntityState.Name ne 'Passed Dev01 QA' and EntityState.Name ne 'Ready to Deploy to prod'`,
369
363
  "include": includeFilter,
370
364
  }
371
365
  });
@@ -373,11 +367,11 @@ export class TpClient {
373
367
  async getReleaseOpenBugs({ name, results = 200, withDescription = false }) {
374
368
  const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
375
369
  return this.get({
376
- pathParam: { "Bugs": '' },
370
+ pathParam: ["Bugs"],
377
371
  param: {
378
372
  "format": "json",
379
373
  "take": results,
380
- "where": `Release.Name eq '${name}' and EntityState.Name ne 'Closed' and EntityState.Name ne 'Done' and EntityState.Name ne 'Passed Dev01  QA' and EntityState.Name ne 'Ready to Deploy to prod'`,
374
+ "where": `Release.Name eq '${name}' and EntityState.Name ne 'Closed' and EntityState.Name ne 'Done' and EntityState.Name ne 'Passed Dev01 QA' and EntityState.Name ne 'Ready to Deploy to prod'`,
381
375
  "include": includeFilter,
382
376
  }
383
377
  });
@@ -385,7 +379,7 @@ export class TpClient {
385
379
  async getReleaseBugs({ name, results = 100, withDescription = false }) {
386
380
  const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
387
381
  return this.get({
388
- pathParam: { "Bugs": '' },
382
+ pathParam: ["Bugs"],
389
383
  param: {
390
384
  "format": "json",
391
385
  "take": results,
@@ -397,7 +391,7 @@ export class TpClient {
397
391
  async getReleaseFeatures({ name, results = 50, withDescription = false }) {
398
392
  const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
399
393
  return this.get({
400
- pathParam: { "Features": '' },
394
+ pathParam: ["Features"],
401
395
  param: {
402
396
  "format": "json",
403
397
  "take": results,
@@ -408,7 +402,7 @@ export class TpClient {
408
402
  }
409
403
  async getFeatureUserStories(featureId) {
410
404
  return this.get({
411
- pathParam: { "features": '' },
405
+ pathParam: ["features"],
412
406
  param: {
413
407
  "format": "json",
414
408
  "where": `(id==${featureId})`,
@@ -419,7 +413,7 @@ export class TpClient {
419
413
  }
420
414
  async getUserStoriesIdsByFeatureId(featureId) {
421
415
  return this.get({
422
- pathParam: { "userstories": '' },
416
+ pathParam: ["userstories"],
423
417
  param: {
424
418
  "format": "json",
425
419
  "where": `(Feature.Id==${featureId})`,
@@ -430,7 +424,7 @@ export class TpClient {
430
424
  }
431
425
  async getUserStoryTestPlan(userStoryId) {
432
426
  return this.get({
433
- pathParam: { "userStories": userStoryId },
427
+ pathParam: ["userStories", userStoryId],
434
428
  param: {
435
429
  "format": "json",
436
430
  "select": `{id,storyName:name,linkedtestplan}`,
@@ -439,18 +433,9 @@ export class TpClient {
439
433
  });
440
434
  }
441
435
  async getCardTestPlan(cardId, resourceType = 'UserStory') {
442
- let requestPath = "";
443
- if (resourceType === 'UserStory') {
444
- requestPath = "userStories";
445
- }
446
- else if (resourceType === 'Bug') {
447
- requestPath = "bugs";
448
- }
449
- else if (resourceType === 'Feature') {
450
- requestPath = "features";
451
- }
436
+ const pathMap = { UserStory: "userStories", Bug: "bugs", Feature: "features" };
452
437
  return this.get({
453
- pathParam: { [requestPath]: cardId },
438
+ pathParam: [pathMap[resourceType], cardId],
454
439
  param: {
455
440
  "format": "json",
456
441
  "select": `{id,linkedtestplan}`,
@@ -459,38 +444,44 @@ export class TpClient {
459
444
  }
460
445
  async getTestPlanTestCases(testPlanId) {
461
446
  return this.get({
462
- pathParam: {
463
- "testPlans": testPlanId,
464
- "testcases": "",
465
- },
447
+ pathParam: ["testPlans", testPlanId, "testcases"],
466
448
  param: { "format": "json" },
467
449
  });
468
450
  }
469
451
  async getTestCaseSteps(testCaseId) {
470
452
  return this.get({
471
- pathParam: {
472
- "testCases": testCaseId,
473
- "teststeps": "",
474
- },
475
- param: { "format": "json", },
453
+ pathParam: ["testCases", testCaseId, "teststeps"],
454
+ param: { "format": "json" },
476
455
  });
477
456
  }
478
457
  async getProjects() {
479
458
  return this.get({
480
- pathParam: { "Projects": '' },
459
+ pathParam: ["Projects"],
481
460
  param: { "format": "json" },
482
461
  });
483
462
  }
484
463
  async getTeams() {
485
464
  return this.get({
486
- pathParam: { "Teams": '' },
465
+ pathParam: ["Teams"],
487
466
  param: { "format": "json" },
488
467
  });
489
468
  }
469
+ async getCardStatus(cardId, resourceType = 'UserStory') {
470
+ const pathMap = { UserStory: 'userStory', Bug: 'bug', Feature: 'feature' };
471
+ return this.get({
472
+ pathParam: [pathMap[resourceType]],
473
+ param: {
474
+ "select": `{Project:{Project.Id},EntityState:{EntityState.Id,EntityState.Name,EntityState.NextStates,EntityState.Workflow.Id as WorkflowId},TeamState:{ResponsibleTeam.Id,Team:{ResponsibleTeam.Team.Id,ResponsibleTeam.Team.Name},EntityState:{ResponsibleTeam.EntityState.Id,ResponsibleTeam.EntityState.Name,ResponsibleTeam.EntityState.Workflow.Id as WorkflowId}},AssignedTeams.Select({TeamAssignmentId:Id,Id:Team.Id,Name:Team.Name}) as Teams}`,
475
+ "where": `(id=${cardId})`,
476
+ "take": "1",
477
+ },
478
+ apiVersion: this.v2
479
+ });
480
+ }
490
481
  async getContext() {
491
482
  return this.get({
492
- pathParam: { "Context": '' },
493
- param: { "format": "json", }
483
+ pathParam: ["Context"],
484
+ param: { "format": "json" }
494
485
  });
495
486
  }
496
487
  async addAttachedFile(generalId, source) {
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "engines": {
26
26
  "node": ">=20.x"
27
27
  },
28
- "version": "2.2.3a",
28
+ "version": "2.2.4",
29
29
  "description": "MCP server for Tartget Process",
30
30
  "main": "build/index.js",
31
31
  "keywords": [