targetprocess-mcp-server 2.4.4 → 2.5.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.
package/README.md CHANGED
@@ -96,6 +96,9 @@ Cards — Write
96
96
  > `projectId` and `teamId` are optional — fall back to `TP_PROJECT_ID` and `TP_TEAM_ID` from config
97
97
  - `format_existing_user_story` — Re-format the description of an existing user story using the structured template (id, header object with asA/iWant/soThat, acceptanceCriteria array, scenarios array with Gherkin steps, optional title, definitions, examplesTable, edgeCases, references, notes)
98
98
  > Call `get_user_story_content` first to read the current content, then reconstruct the structured fields before calling this tool
99
+ - `get_epic_content` — Get a Targetprocess Epic by ID, including description, state, and progress (id)
100
+ - `update_epic` — Update an epic's title, description, release, or project (id, optional title, optional description, optional releaseId, optional projectId)
101
+ - `get_epic_features` — Get all features belonging to an epic (id)
99
102
  - `create_feature` — Create a new feature (title, optional description, optional epicId, optional releaseId, optional projectId, optional teamId)
100
103
  > [!NOTE]
101
104
  > `projectId` and `teamId` are optional — fall back to `TP_PROJECT_ID` and `TP_TEAM_ID` from config
@@ -129,6 +132,11 @@ Time Tracking
129
132
  - `log_time` — Log time spent on a Task, User Story, or Bug (entityId, entityType: Task | UserStory | Bug, hours, optional description, optional date)
130
133
  - `get_my_time_logs` — Get recent time log entries submitted by the current user (optional take)
131
134
 
135
+ Assignments
136
+ - `assign_role` — Assign a user to a role (e.g. Business Analyst, Developer, QA Engineer) on a TP card (cardId, userId, roleId)
137
+ - `assign_role_to_feature` — Assign a user to a role on all user stories in a feature in one call (featureId, userId, roleId)
138
+ - `get_assignment_roles` — List all available assignment roles with their IDs
139
+
132
140
  Developer Tools
133
141
  - `get_commit_message` — Returns a formatted commit message string for a task or bug ID (id, type: task | bug)
134
142
  > Format for task on a user story: `F#<featureId> US#<userStoryId> T#<taskId> <title>`
@@ -0,0 +1,14 @@
1
+ export async function handleCreateEpic(tp, params) {
2
+ const response = await tp.createEpic(params);
3
+ if (!response) {
4
+ return {
5
+ content: [{
6
+ type: 'text',
7
+ text: `Failed to create epic "${params.title}"\n JSON: ${JSON.stringify(response, null, 2)}`
8
+ }],
9
+ };
10
+ }
11
+ return {
12
+ content: [{ type: 'text', text: JSON.stringify(response) }],
13
+ };
14
+ }
@@ -0,0 +1,38 @@
1
+ import { JSDOM } from 'jsdom';
2
+ export async function handleGetEpicContent(tp, id) {
3
+ const epic = await tp.getEpic(id);
4
+ if (!epic) {
5
+ return {
6
+ content: [{
7
+ type: 'text',
8
+ text: `Failed to get epic, id: ${id}\n JSON: ${JSON.stringify(epic, null, 2)}`
9
+ }],
10
+ };
11
+ }
12
+ const result = {
13
+ name: epic.Name,
14
+ id: epic.Id,
15
+ description: '',
16
+ entityState: epic.EntityState?.Name,
17
+ release: epic.Release?.Name,
18
+ portfolioEpic: epic.PortfolioEpic?.Name,
19
+ progress: epic.Progress,
20
+ effort: epic.Effort,
21
+ customFields: epic.CustomFields,
22
+ };
23
+ const description = epic.Description || '';
24
+ if (description) {
25
+ try {
26
+ const dom = new JSDOM(`<html><body><div id="content">${description}</div></body></html>`);
27
+ const text = dom.window.document.getElementById('content')?.textContent;
28
+ if (text)
29
+ result.description = text;
30
+ }
31
+ catch (error) {
32
+ console.error('Error parsing epic description:', error);
33
+ }
34
+ }
35
+ return {
36
+ content: [{ type: 'text', text: JSON.stringify(result) }],
37
+ };
38
+ }
@@ -0,0 +1,24 @@
1
+ export async function handleGetEpicFeatures(tp, epicId) {
2
+ const response = await tp.getEpicFeatures(epicId);
3
+ if (!response) {
4
+ return {
5
+ content: [{
6
+ type: 'text',
7
+ text: `Failed to get features for epic id: ${epicId}`
8
+ }],
9
+ };
10
+ }
11
+ const features = response.Items ?? [];
12
+ const result = features.map(f => ({
13
+ id: f.Id,
14
+ name: f.Name,
15
+ entityState: f.EntityState?.Name,
16
+ team: f.Team?.Name,
17
+ release: f.Release?.Name,
18
+ progress: f.Progress,
19
+ effort: f.Effort,
20
+ }));
21
+ return {
22
+ content: [{ type: 'text', text: JSON.stringify({ total: result.length, features: result }) }],
23
+ };
24
+ }
@@ -0,0 +1,14 @@
1
+ export async function handleUpdateEpic(tp, params) {
2
+ const response = await tp.updateEpic(params);
3
+ if (!response) {
4
+ return {
5
+ content: [{
6
+ type: 'text',
7
+ text: `Failed to update epic id: ${params.id}\n JSON: ${JSON.stringify(response, null, 2)}`
8
+ }],
9
+ };
10
+ }
11
+ return {
12
+ content: [{ type: 'text', text: JSON.stringify(response) }],
13
+ };
14
+ }
package/build/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { z } from "zod";
4
4
  import { JSDOM } from "jsdom";
5
+ import { createRequire } from "module";
5
6
  import { TpClient } from "./tp.js";
6
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
8
  import { config } from "./config.js";
@@ -25,6 +26,10 @@ import { handleGetBugComments } from "./handlers/get_bug_comments.js";
25
26
  import { handleCreateBug } from "./handlers/create_bug.js";
26
27
  import { handleCreateUserStory } from "./handlers/create_user_story.js";
27
28
  import { handleCreateFeature } from "./handlers/create_feature.js";
29
+ import { handleCreateEpic } from "./handlers/create_epic.js";
30
+ import { handleGetEpicContent } from "./handlers/get_epic_content.js";
31
+ import { handleUpdateEpic } from "./handlers/update_epic.js";
32
+ import { handleGetEpicFeatures } from "./handlers/get_epic_features.js";
28
33
  import { handleCreateTask } from "./handlers/create_task.js";
29
34
  import { handleUpdateBug } from "./handlers/update_bug.js";
30
35
  import { handleGetInProgressTasksAndBugs } from "./handlers/get_in_progress_tasks_and_bugs.js";
@@ -474,9 +479,12 @@ server.registerTool('update_user_story', {
474
479
  entityStateId: z.string()
475
480
  .optional()
476
481
  .describe('Optional Entity State ID — if user gave a state name, resolve it via "get_user_story_workflows" first'),
482
+ featureId: z.string()
483
+ .optional()
484
+ .describe('Optional Feature ID — moves this user story to the specified feature'),
477
485
  },
478
- }, async ({ id, title, description, projectId, teamId, entityStateId }) => {
479
- const response = await tp.updateUserStory({ id, title, description, projectId, teamId, entityStateId });
486
+ }, async ({ id, title, description, projectId, teamId, entityStateId, featureId }) => {
487
+ const response = await tp.updateUserStory({ id, title, description, projectId, teamId, entityStateId, featureId });
480
488
  if (!response) {
481
489
  return {
482
490
  content: [{
@@ -718,6 +726,69 @@ server.registerTool('create_feature', {
718
726
  .describe('Optional Team ID — defaults to TP_TEAM_ID from config'),
719
727
  },
720
728
  }, async ({ title, description, epicId, releaseId, projectId, teamId }) => handleCreateFeature(tp, { title, description, epicId, releaseId, projectId, teamId }));
729
+ server.registerTool('create_epic', {
730
+ title: 'Create a new epic',
731
+ description: `Create a new Epic in Targetprocess.`,
732
+ inputSchema: {
733
+ title: z.string()
734
+ .describe('Epic title'),
735
+ description: z.string()
736
+ .optional()
737
+ .describe('Optional epic description (when provided, format as HTML)'),
738
+ releaseId: z.string()
739
+ .min(5)
740
+ .max(6)
741
+ .optional()
742
+ .describe('Optional Release ID to link this epic to (e.g. 145200)'),
743
+ projectId: z.string()
744
+ .optional()
745
+ .describe('Optional Project ID -- defaults to TP_PROJECT_ID from config'),
746
+ },
747
+ }, async ({ title, description, releaseId, projectId }) => handleCreateEpic(tp, { title, description, releaseId, projectId }));
748
+ server.registerTool('get_epic_content', {
749
+ title: 'Get epic content',
750
+ description: 'Get a Targetprocess Epic by ID, including its description, state, and progress.',
751
+ inputSchema: {
752
+ id: z.string()
753
+ .min(5)
754
+ .max(6)
755
+ .describe('Epic ID (e.g. 148813)'),
756
+ },
757
+ }, async ({ id }) => handleGetEpicContent(tp, id));
758
+ server.registerTool('update_epic', {
759
+ title: 'Update an epic',
760
+ description: 'Update a Targetprocess Epic. Pass only the fields to change.',
761
+ inputSchema: {
762
+ id: z.string()
763
+ .min(5)
764
+ .max(6)
765
+ .describe('Epic ID (e.g. 148813)'),
766
+ title: z.string()
767
+ .optional()
768
+ .describe('Updated epic title'),
769
+ description: z.string()
770
+ .optional()
771
+ .describe('Updated epic description (format as HTML)'),
772
+ releaseId: z.string()
773
+ .min(5)
774
+ .max(6)
775
+ .optional()
776
+ .describe('Optional Release ID to link this epic to'),
777
+ projectId: z.string()
778
+ .optional()
779
+ .describe('Optional Project ID'),
780
+ },
781
+ }, async ({ id, title, description, releaseId, projectId }) => handleUpdateEpic(tp, { id, title, description, releaseId, projectId }));
782
+ server.registerTool('get_epic_features', {
783
+ title: 'Get features in an epic',
784
+ description: 'Get all Features belonging to a Targetprocess Epic.',
785
+ inputSchema: {
786
+ id: z.string()
787
+ .min(5)
788
+ .max(6)
789
+ .describe('Epic ID (e.g. 148813)'),
790
+ },
791
+ }, async ({ id }) => handleGetEpicFeatures(tp, id));
721
792
  server.registerTool('create_test_plan', {
722
793
  title: 'Create a new test plan linked to a TP card',
723
794
  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.`,
@@ -1380,6 +1451,15 @@ server.registerTool('get_my_time_logs', {
1380
1451
  .describe('Number of entries to return, default is 25'),
1381
1452
  },
1382
1453
  }, async ({ take }) => handleGetMyTimeLogs(tp, take));
1454
+ const require = createRequire(import.meta.url);
1455
+ const { version } = require("../package.json");
1456
+ server.registerTool('get_version', {
1457
+ title: 'Get server version',
1458
+ description: 'Returns the current version of the MCP server from package.json.',
1459
+ inputSchema: {},
1460
+ }, async () => ({
1461
+ content: [{ type: "text", text: version }]
1462
+ }));
1383
1463
  async function main() {
1384
1464
  const transport = new StdioServerTransport();
1385
1465
  await server.connect(transport);
package/build/tp.js CHANGED
@@ -197,7 +197,7 @@ export class TpClient {
197
197
  param: { "format": "json" },
198
198
  }, userStory);
199
199
  }
200
- async updateUserStory({ id, title, description, projectId, teamId, entityStateId }) {
200
+ async updateUserStory({ id, title, description, projectId, teamId, entityStateId, featureId }) {
201
201
  const userStory = { "Id": id };
202
202
  if (title)
203
203
  userStory["Name"] = title;
@@ -209,6 +209,8 @@ export class TpClient {
209
209
  userStory["assignedTeams"] = [{ "team": { "id": teamId } }];
210
210
  if (entityStateId)
211
211
  userStory["EntityState"] = { "Id": entityStateId };
212
+ if (featureId)
213
+ userStory["Feature"] = { "Id": featureId };
212
214
  return this.post({
213
215
  pathParam: ["UserStories"],
214
216
  param: { "format": "json" },
@@ -283,6 +285,52 @@ export class TpClient {
283
285
  param: { "format": "json" },
284
286
  }, userStory);
285
287
  }
288
+ async getEpic(epicId) {
289
+ return this.get({
290
+ pathParam: ["Epics", epicId],
291
+ param: { "format": "json" },
292
+ });
293
+ }
294
+ async updateEpic({ id, title, description, releaseId, projectId }) {
295
+ const epic = { "Id": id };
296
+ if (title)
297
+ epic["Name"] = title;
298
+ if (description)
299
+ epic["Description"] = description;
300
+ if (projectId)
301
+ epic["Project"] = { "Id": projectId };
302
+ if (releaseId)
303
+ epic["Release"] = { "Id": releaseId };
304
+ return this.post({
305
+ pathParam: ["Epics"],
306
+ param: { "format": "json" },
307
+ }, epic);
308
+ }
309
+ async getEpicFeatures(epicId) {
310
+ return this.get({
311
+ pathParam: ["Features"],
312
+ param: {
313
+ "format": "json",
314
+ "where": `Epic.Id eq ${epicId}`,
315
+ "include": "[Id,Name,Description,EntityState[Name],Team[Name],Release[Name],Progress,Effort]",
316
+ "take": 100,
317
+ },
318
+ });
319
+ }
320
+ async createEpic({ title, description, releaseId, projectId }) {
321
+ const epic = {
322
+ "Name": title,
323
+ "Project": { "Id": projectId || config.tp.projectId },
324
+ };
325
+ if (description)
326
+ epic["Description"] = description;
327
+ if (releaseId)
328
+ epic["Release"] = { "Id": releaseId };
329
+ return this.post({
330
+ pathParam: ["Epics"],
331
+ param: { "format": "json" },
332
+ }, epic);
333
+ }
286
334
  async createFeature({ title, description, epicId, releaseId, projectId, teamId }) {
287
335
  const feature = {
288
336
  "Name": title,
@@ -772,6 +820,38 @@ export class TpClient {
772
820
  param: { "format": "json" },
773
821
  }, body);
774
822
  }
823
+ async assignRole(cardId, userId, roleId) {
824
+ return this.post({
825
+ pathParam: ["Assignments"],
826
+ param: { "format": "json" },
827
+ }, {
828
+ Assignable: { Id: parseInt(cardId) },
829
+ GeneralUser: { Id: parseInt(userId) },
830
+ Role: { Id: parseInt(roleId) },
831
+ });
832
+ }
833
+ async assignRoleToAllStoriesInFeature(featureId, userId, roleId) {
834
+ const storiesResponse = await this.getFeatureUserStories(featureId);
835
+ const storyItems = storiesResponse?.items?.[0]?.userStories?.items ?? [];
836
+ const succeeded = [];
837
+ const failed = [];
838
+ await Promise.all(storyItems.map(async (story) => {
839
+ const result = await this.assignRole(String(story.id), userId, roleId);
840
+ if (result) {
841
+ succeeded.push(result);
842
+ }
843
+ else {
844
+ failed.push(story.id);
845
+ }
846
+ }));
847
+ return { succeeded, failed };
848
+ }
849
+ async getAssignmentRoles() {
850
+ return this.get({
851
+ pathParam: ["Roles"],
852
+ param: { "format": "json" },
853
+ });
854
+ }
775
855
  async getMyTimeLogs(take = 25) {
776
856
  return this.get({
777
857
  pathParam: ["Times"],
package/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  "engines": {
28
28
  "node": ">=20.x"
29
29
  },
30
- "version": "2.4.4",
30
+ "version": "2.5.1",
31
31
  "description": "MCP server for Tartget Process",
32
32
  "main": "build/index.js",
33
33
  "keywords": [