targetprocess-mcp-server 2.4.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -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
@@ -25,6 +25,10 @@ import { handleGetBugComments } from "./handlers/get_bug_comments.js";
25
25
  import { handleCreateBug } from "./handlers/create_bug.js";
26
26
  import { handleCreateUserStory } from "./handlers/create_user_story.js";
27
27
  import { handleCreateFeature } from "./handlers/create_feature.js";
28
+ import { handleCreateEpic } from "./handlers/create_epic.js";
29
+ import { handleGetEpicContent } from "./handlers/get_epic_content.js";
30
+ import { handleUpdateEpic } from "./handlers/update_epic.js";
31
+ import { handleGetEpicFeatures } from "./handlers/get_epic_features.js";
28
32
  import { handleCreateTask } from "./handlers/create_task.js";
29
33
  import { handleUpdateBug } from "./handlers/update_bug.js";
30
34
  import { handleGetInProgressTasksAndBugs } from "./handlers/get_in_progress_tasks_and_bugs.js";
@@ -718,6 +722,69 @@ server.registerTool('create_feature', {
718
722
  .describe('Optional Team ID — defaults to TP_TEAM_ID from config'),
719
723
  },
720
724
  }, async ({ title, description, epicId, releaseId, projectId, teamId }) => handleCreateFeature(tp, { title, description, epicId, releaseId, projectId, teamId }));
725
+ server.registerTool('create_epic', {
726
+ title: 'Create a new epic',
727
+ description: `Create a new Epic in Targetprocess.`,
728
+ inputSchema: {
729
+ title: z.string()
730
+ .describe('Epic title'),
731
+ description: z.string()
732
+ .optional()
733
+ .describe('Optional epic description (when provided, format as HTML)'),
734
+ releaseId: z.string()
735
+ .min(5)
736
+ .max(6)
737
+ .optional()
738
+ .describe('Optional Release ID to link this epic to (e.g. 145200)'),
739
+ projectId: z.string()
740
+ .optional()
741
+ .describe('Optional Project ID -- defaults to TP_PROJECT_ID from config'),
742
+ },
743
+ }, async ({ title, description, releaseId, projectId }) => handleCreateEpic(tp, { title, description, releaseId, projectId }));
744
+ server.registerTool('get_epic_content', {
745
+ title: 'Get epic content',
746
+ description: 'Get a Targetprocess Epic by ID, including its description, state, and progress.',
747
+ inputSchema: {
748
+ id: z.string()
749
+ .min(5)
750
+ .max(6)
751
+ .describe('Epic ID (e.g. 148813)'),
752
+ },
753
+ }, async ({ id }) => handleGetEpicContent(tp, id));
754
+ server.registerTool('update_epic', {
755
+ title: 'Update an epic',
756
+ description: 'Update a Targetprocess Epic. Pass only the fields to change.',
757
+ inputSchema: {
758
+ id: z.string()
759
+ .min(5)
760
+ .max(6)
761
+ .describe('Epic ID (e.g. 148813)'),
762
+ title: z.string()
763
+ .optional()
764
+ .describe('Updated epic title'),
765
+ description: z.string()
766
+ .optional()
767
+ .describe('Updated epic description (format as HTML)'),
768
+ releaseId: z.string()
769
+ .min(5)
770
+ .max(6)
771
+ .optional()
772
+ .describe('Optional Release ID to link this epic to'),
773
+ projectId: z.string()
774
+ .optional()
775
+ .describe('Optional Project ID'),
776
+ },
777
+ }, async ({ id, title, description, releaseId, projectId }) => handleUpdateEpic(tp, { id, title, description, releaseId, projectId }));
778
+ server.registerTool('get_epic_features', {
779
+ title: 'Get features in an epic',
780
+ description: 'Get all Features belonging to a Targetprocess Epic.',
781
+ inputSchema: {
782
+ id: z.string()
783
+ .min(5)
784
+ .max(6)
785
+ .describe('Epic ID (e.g. 148813)'),
786
+ },
787
+ }, async ({ id }) => handleGetEpicFeatures(tp, id));
721
788
  server.registerTool('create_test_plan', {
722
789
  title: 'Create a new test plan linked to a TP card',
723
790
  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.`,
package/build/tp.js CHANGED
@@ -283,6 +283,52 @@ export class TpClient {
283
283
  param: { "format": "json" },
284
284
  }, userStory);
285
285
  }
286
+ async getEpic(epicId) {
287
+ return this.get({
288
+ pathParam: ["Epics", epicId],
289
+ param: { "format": "json" },
290
+ });
291
+ }
292
+ async updateEpic({ id, title, description, releaseId, projectId }) {
293
+ const epic = { "Id": id };
294
+ if (title)
295
+ epic["Name"] = title;
296
+ if (description)
297
+ epic["Description"] = description;
298
+ if (projectId)
299
+ epic["Project"] = { "Id": projectId };
300
+ if (releaseId)
301
+ epic["Release"] = { "Id": releaseId };
302
+ return this.post({
303
+ pathParam: ["Epics"],
304
+ param: { "format": "json" },
305
+ }, epic);
306
+ }
307
+ async getEpicFeatures(epicId) {
308
+ return this.get({
309
+ pathParam: ["Features"],
310
+ param: {
311
+ "format": "json",
312
+ "where": `Epic.Id eq ${epicId}`,
313
+ "include": "[Id,Name,Description,EntityState[Name],Team[Name],Release[Name],Progress,Effort]",
314
+ "take": 100,
315
+ },
316
+ });
317
+ }
318
+ async createEpic({ title, description, releaseId, projectId }) {
319
+ const epic = {
320
+ "Name": title,
321
+ "Project": { "Id": projectId || config.tp.projectId },
322
+ };
323
+ if (description)
324
+ epic["Description"] = description;
325
+ if (releaseId)
326
+ epic["Release"] = { "Id": releaseId };
327
+ return this.post({
328
+ pathParam: ["Epics"],
329
+ param: { "format": "json" },
330
+ }, epic);
331
+ }
286
332
  async createFeature({ title, description, epicId, releaseId, projectId, teamId }) {
287
333
  const feature = {
288
334
  "Name": title,
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.0",
31
31
  "description": "MCP server for Tartget Process",
32
32
  "main": "build/index.js",
33
33
  "keywords": [