targetprocess-mcp-server 2.6.3 → 2.6.4-a
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/build/handlers/get_feature_comments.js +45 -0
- package/build/index.js +61 -3
- package/build/tp.js +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
export async function handleGetFeatureComments(tp, id, results) {
|
|
3
|
+
const response = await tp.getFeatureComments(id, results);
|
|
4
|
+
if (!response) {
|
|
5
|
+
return {
|
|
6
|
+
content: [{
|
|
7
|
+
type: 'text',
|
|
8
|
+
text: `Failed to get comments for feature id: ${id}`
|
|
9
|
+
}],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const items = response.Items || [];
|
|
13
|
+
if (items.length === 0) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: `No comments found for feature id: ${id}`,
|
|
18
|
+
}],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const parsedItems = items.map((item) => {
|
|
23
|
+
const dom = new JSDOM(`<html><body><div id="content">${item.Description}</div></body></html>`);
|
|
24
|
+
const descriptionText = dom.window.document.getElementById('content')?.textContent;
|
|
25
|
+
return {
|
|
26
|
+
id: item.Id,
|
|
27
|
+
description: descriptionText,
|
|
28
|
+
createDate: item.CreateDate,
|
|
29
|
+
owner: item.Owner.FullName,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: 'text', text: JSON.stringify(parsedItems) }],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error('Error parsing feature comments:', error);
|
|
38
|
+
return {
|
|
39
|
+
content: [{
|
|
40
|
+
type: 'text',
|
|
41
|
+
text: `Failed to parse feature comments for feature id: ${id}`,
|
|
42
|
+
}],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
package/build/index.js
CHANGED
|
@@ -43,6 +43,7 @@ import { handleLogTime } from "./handlers/log_time.js";
|
|
|
43
43
|
import { handleGetMyTimeLogs } from "./handlers/get_my_time_logs.js";
|
|
44
44
|
import { handleGetFeatureUserStories } from "./handlers/get_feature_user_stories.js";
|
|
45
45
|
import { handleGetFeatureContent } from "./handlers/get_feature_content.js";
|
|
46
|
+
import { handleGetFeatureComments } from "./handlers/get_feature_comments.js";
|
|
46
47
|
import { handleGetUserStoryBugs } from "./handlers/get_user_story_bugs.js";
|
|
47
48
|
import { handleGetCardCurrentStatus } from "./handlers/get_card_current_status.js";
|
|
48
49
|
import { handleUpdateUserStorySubState } from "./handlers/update_user_story_sub_state.js";
|
|
@@ -449,7 +450,7 @@ server.registerTool('update_bug', {
|
|
|
449
450
|
.describe('Optional Team ID — if user gave a team name, resolve it via "get_teams" first; defaults to TP_TEAM_ID from config'),
|
|
450
451
|
entityStateId: z.string()
|
|
451
452
|
.optional()
|
|
452
|
-
.describe('Optional Entity State ID — if user gave a state name, resolve it via "get_bug_workflows" first; defaults to "
|
|
453
|
+
.describe('Optional Entity State ID — if user gave a state name, resolve it via "get_bug_workflows" first; defaults to "Backlog"'),
|
|
453
454
|
tags: z.string()
|
|
454
455
|
.optional()
|
|
455
456
|
.describe('Optional comma-separated tags to apply, e.g. "regression, mobile"'),
|
|
@@ -718,7 +719,12 @@ server.registerTool('create_formatted_feature', {
|
|
|
718
719
|
1) Header — "businessBackground" is a 1-2 sentence value statement: who benefits and why;
|
|
719
720
|
2) Definitions — cross-cutting terms used across multiple child stories, so they aren't redefined at every story level; if none apply, OMIT "definitions" entirely (do not send an empty section);
|
|
720
721
|
3) Scope & Boundaries — what this feature explicitly includes/excludes, to stop child stories drifting into adjacent features; omit if genuinely trivial;
|
|
721
|
-
4) Non-Functional Requirements ("nonFunctionalRequirements") —
|
|
722
|
+
4) Non-Functional Requirements ("nonFunctionalRequirements") — this is the core of the template and doubles as ISO 27001:2022 audit evidence that NFRs (including information security, per Annex A Control 5.8) are considered at design time, not bolted on after. EVERY feature MUST include these four fixed rows, worded exactly as below (do not reword the questions — consistency in the questions is what makes them auditable), each answered SPECIFICALLY for this feature in "storyOrOwner" — a real, checkable answer (e.g. which systems/data/roles are involved), never a generic "considered" placeholder:
|
|
723
|
+
- area "System performance" / requirement "What does this feature add or change in terms of load, queries, polling, rendering etc., and what's the expected impact, if any?";
|
|
724
|
+
- area "Information security (ISO 27001:2022 Annex A 5.8)" / requirement "What personal or sensitive data does this feature touch, and what protects its privacy, confidentiality, integrity and availability?";
|
|
725
|
+
- area "Quality control" / requirement "How will this feature's behaviour and failures be measured or tested, and what happens when it fails?";
|
|
726
|
+
- area "Service logging" / requirement "What events or states should be logged so product support can diagnose an issue without reproducing it manually?";
|
|
727
|
+
For each row, set status to "Covered" (storyOrOwner holds the specific answer, or the child story/scenario that proves it), "Gap" (no answer/story yet — storyOrOwner names who should follow up), or "Decision needed" (genuinely open, not answerable yet). If any of the four is unclear, follow-up owner in rather than guessing — he's the named escalation contact, not a mandatory sign-off gate. Additional NFR categories beyond these four (Compliance, Billing, Operational, etc.) can still be added as extra rows when relevant, using the same Covered/Gap/Decision-needed treatment;
|
|
722
728
|
5) Cross-Cutting Scenarios — ONLY for behavior spanning multiple child stories that wouldn't naturally sit in any one of them (e.g. tenant isolation across all stories); do not duplicate per-story Gherkin here; omit if none apply;
|
|
723
729
|
6) Child Stories ("childStories") — pull this from "get_feature_user_stories" / "get_not_covered_user_stories_in_feature" rather than retyping it; keep it as a live pointer, not a duplicate spec; normally empty when first creating the feature;
|
|
724
730
|
7) Open Questions / Risks ("openQuestions") — anything raised at feature conception that hasn't been resolved into either a Covered NFR row or a child story; this is the section most likely to get silently dropped — treat it as the running "not done yet" list until each line is promoted to a Covered NFR row;
|
|
@@ -770,7 +776,7 @@ server.registerTool('create_formatted_feature', {
|
|
|
770
776
|
.describe('If Covered, the child story ID/scenario that proves it; if Gap or Decision needed, who owns the follow-up (e.g. "Needs legal/BA follow-up")'),
|
|
771
777
|
}))
|
|
772
778
|
.min(1)
|
|
773
|
-
.describe('Every NFR category converted from prose into a testable/decided row — the core of this template. Do not leave requirements as untested prose'),
|
|
779
|
+
.describe('Every NFR category converted from prose into a testable/decided row — the core of this template. MUST include the four fixed rows required for ISO 27001:2022 audit evidence: "System performance", "Information security (ISO 27001:2022 Annex A 5.8)", "Quality control", and "Service logging" — worded exactly, each answered specifically for this feature (escalate unclear items to story owner). Additional categories may be added beyond these four. Do not leave requirements as untested prose'),
|
|
774
780
|
crossCuttingScenarios: z.array(z.object({
|
|
775
781
|
name: z.string()
|
|
776
782
|
.describe('Scenario name'),
|
|
@@ -1099,6 +1105,58 @@ server.registerTool('get_feature_content', {
|
|
|
1099
1105
|
.describe('TP feature ID (e.g. 145636)'),
|
|
1100
1106
|
},
|
|
1101
1107
|
}, async ({ id }) => handleGetFeatureContent(tp, id));
|
|
1108
|
+
server.registerTool('get_feature_comments', {
|
|
1109
|
+
title: 'Get feature comments',
|
|
1110
|
+
description: 'Get comments for a TP feature by its ID',
|
|
1111
|
+
inputSchema: {
|
|
1112
|
+
id: z.string()
|
|
1113
|
+
.min(5)
|
|
1114
|
+
.max(9)
|
|
1115
|
+
.describe('TP feature ID (e.g. 145636)'),
|
|
1116
|
+
results: z.number()
|
|
1117
|
+
.default(25)
|
|
1118
|
+
.optional()
|
|
1119
|
+
.describe('Number of comments to return, default is 25'),
|
|
1120
|
+
},
|
|
1121
|
+
}, async ({ id, results }) => handleGetFeatureComments(tp, id, results));
|
|
1122
|
+
server.registerTool('get_assignment_roles', {
|
|
1123
|
+
title: 'Get assignment roles',
|
|
1124
|
+
description: 'Returns all available assignment roles (e.g. Business Analyst, Developer) with their IDs.',
|
|
1125
|
+
inputSchema: {},
|
|
1126
|
+
}, async () => {
|
|
1127
|
+
const result = await tp.getAssignmentRoles();
|
|
1128
|
+
return {
|
|
1129
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1130
|
+
};
|
|
1131
|
+
});
|
|
1132
|
+
server.registerTool('assign_role', {
|
|
1133
|
+
title: 'Assign a role to a user on a card',
|
|
1134
|
+
description: 'Assigns a user to a specific role on a single TP card (User Story, Bug, etc.).',
|
|
1135
|
+
inputSchema: {
|
|
1136
|
+
cardId: z.string().describe('TP card ID (e.g. 149350)'),
|
|
1137
|
+
userId: z.string().describe('TP user ID'),
|
|
1138
|
+
roleId: z.string().describe('TP role ID — use get_assignment_roles to find the right ID'),
|
|
1139
|
+
},
|
|
1140
|
+
}, async ({ cardId, userId, roleId }) => {
|
|
1141
|
+
const result = await tp.assignRole(cardId, userId, roleId);
|
|
1142
|
+
return {
|
|
1143
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1144
|
+
};
|
|
1145
|
+
});
|
|
1146
|
+
server.registerTool('assign_role_to_feature', {
|
|
1147
|
+
title: 'Assign a role to a user on all user stories in a feature',
|
|
1148
|
+
description: 'Assigns a user to a specific role on every user story within a given feature.',
|
|
1149
|
+
inputSchema: {
|
|
1150
|
+
featureId: z.string().describe('TP feature ID (e.g. 149341)'),
|
|
1151
|
+
userId: z.string().describe('TP user ID'),
|
|
1152
|
+
roleId: z.string().describe('TP role ID — use get_assignment_roles to find the right ID'),
|
|
1153
|
+
},
|
|
1154
|
+
}, async ({ featureId, userId, roleId }) => {
|
|
1155
|
+
const result = await tp.assignRoleToAllStoriesInFeature(featureId, userId, roleId);
|
|
1156
|
+
return {
|
|
1157
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1158
|
+
};
|
|
1159
|
+
});
|
|
1102
1160
|
server.registerTool('get_user_story_bugs', {
|
|
1103
1161
|
title: 'Get user story bugs',
|
|
1104
1162
|
description: 'Get bugs linked to a TP user story by its ID',
|
package/build/tp.js
CHANGED
|
@@ -586,6 +586,16 @@ export class TpClient {
|
|
|
586
586
|
});
|
|
587
587
|
return response;
|
|
588
588
|
}
|
|
589
|
+
async getFeatureComments(featureId, results = 25) {
|
|
590
|
+
const response = await this.get({
|
|
591
|
+
pathParam: ["Features", featureId, "Comments"],
|
|
592
|
+
param: {
|
|
593
|
+
"format": "json",
|
|
594
|
+
"take": results,
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
return response;
|
|
598
|
+
}
|
|
589
599
|
async searchContainsNameText({ text, entityType }) {
|
|
590
600
|
return this.get({
|
|
591
601
|
pathParam: [entityType],
|