@k-system/tickr-mcp 0.2.0 → 0.3.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/dist/index.js +55 -1
- package/dist/tools/add-project-member.d.ts +4 -0
- package/dist/tools/add-project-member.js +32 -0
- package/dist/tools/create-label.d.ts +4 -0
- package/dist/tools/create-label.js +30 -0
- package/dist/tools/create-ticket.js +2 -0
- package/dist/tools/delete-comment.d.ts +4 -0
- package/dist/tools/delete-comment.js +22 -0
- package/dist/tools/delete-cycle.d.ts +4 -0
- package/dist/tools/delete-cycle.js +26 -0
- package/dist/tools/delete-epic.d.ts +4 -0
- package/dist/tools/delete-epic.js +26 -0
- package/dist/tools/delete-label.d.ts +4 -0
- package/dist/tools/delete-label.js +26 -0
- package/dist/tools/fail-dev-task.d.ts +4 -0
- package/dist/tools/fail-dev-task.js +29 -0
- package/dist/tools/get-next-statuses.d.ts +4 -0
- package/dist/tools/get-next-statuses.js +30 -0
- package/dist/tools/list-dev-assignments.d.ts +4 -0
- package/dist/tools/list-dev-assignments.js +27 -0
- package/dist/tools/list-project-members.d.ts +4 -0
- package/dist/tools/list-project-members.js +29 -0
- package/dist/tools/list-relations.d.ts +4 -0
- package/dist/tools/list-relations.js +30 -0
- package/dist/tools/list-workflow-presets.d.ts +4 -0
- package/dist/tools/list-workflow-presets.js +22 -0
- package/dist/tools/remove-project-member.d.ts +4 -0
- package/dist/tools/remove-project-member.js +26 -0
- package/dist/tools/update-comment.d.ts +4 -0
- package/dist/tools/update-comment.js +24 -0
- package/dist/tools/update-cycle.d.ts +4 -0
- package/dist/tools/update-cycle.js +39 -0
- package/dist/tools/update-epic.d.ts +4 -0
- package/dist/tools/update-epic.js +42 -0
- package/dist/tools/update-label.d.ts +4 -0
- package/dist/tools/update-label.js +33 -0
- package/dist/tools/update-project-member-role.d.ts +4 -0
- package/dist/tools/update-project-member-role.js +29 -0
- package/dist/tools/update-status.d.ts +4 -0
- package/dist/tools/update-status.js +39 -0
- package/dist/tools/update-ticket.js +12 -4
- package/dist/tools/update-transitions.d.ts +4 -0
- package/dist/tools/update-transitions.js +37 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -50,6 +50,33 @@ import { registerCreateEpic } from "./tools/create-epic.js";
|
|
|
50
50
|
// Phase 2 — Skupina F: Implementation Items
|
|
51
51
|
import { registerCreateImplementationItem } from "./tools/create-implementation-item.js";
|
|
52
52
|
import { registerDeleteImplementationItem } from "./tools/delete-implementation-item.js";
|
|
53
|
+
// Phase 3 — Skupina A: Workflow & Status
|
|
54
|
+
import { registerUpdateTransitions } from "./tools/update-transitions.js";
|
|
55
|
+
import { registerUpdateStatus } from "./tools/update-status.js";
|
|
56
|
+
import { registerListWorkflowPresets } from "./tools/list-workflow-presets.js";
|
|
57
|
+
import { registerGetNextStatuses } from "./tools/get-next-statuses.js";
|
|
58
|
+
// Phase 3 — Skupina B: Comments & Activity
|
|
59
|
+
import { registerUpdateComment } from "./tools/update-comment.js";
|
|
60
|
+
import { registerDeleteComment } from "./tools/delete-comment.js";
|
|
61
|
+
// Phase 3 — Skupina C: Relations
|
|
62
|
+
import { registerListRelations } from "./tools/list-relations.js";
|
|
63
|
+
// Phase 3 — Skupina D: Dev Queue
|
|
64
|
+
import { registerFailDevTask } from "./tools/fail-dev-task.js";
|
|
65
|
+
import { registerListDevAssignments } from "./tools/list-dev-assignments.js";
|
|
66
|
+
// Phase 3 — Skupina E: Labels CRUD
|
|
67
|
+
import { registerCreateLabel } from "./tools/create-label.js";
|
|
68
|
+
import { registerUpdateLabel } from "./tools/update-label.js";
|
|
69
|
+
import { registerDeleteLabel } from "./tools/delete-label.js";
|
|
70
|
+
// Phase 3 — Skupina F: Cycles/Epics extras
|
|
71
|
+
import { registerUpdateCycle } from "./tools/update-cycle.js";
|
|
72
|
+
import { registerDeleteCycle } from "./tools/delete-cycle.js";
|
|
73
|
+
import { registerUpdateEpic } from "./tools/update-epic.js";
|
|
74
|
+
import { registerDeleteEpic } from "./tools/delete-epic.js";
|
|
75
|
+
// ADR-0026 Phase 2f: Project Members
|
|
76
|
+
import { registerListProjectMembers } from "./tools/list-project-members.js";
|
|
77
|
+
import { registerAddProjectMember } from "./tools/add-project-member.js";
|
|
78
|
+
import { registerUpdateProjectMemberRole } from "./tools/update-project-member-role.js";
|
|
79
|
+
import { registerRemoveProjectMember } from "./tools/remove-project-member.js";
|
|
53
80
|
// Resources
|
|
54
81
|
import { registerTicketResource } from "./resources/ticket-resource.js";
|
|
55
82
|
import { registerProjectResource } from "./resources/project-resource.js";
|
|
@@ -58,7 +85,7 @@ async function main() {
|
|
|
58
85
|
const api = new ApiClient(config);
|
|
59
86
|
const server = new McpServer({
|
|
60
87
|
name: "tickr",
|
|
61
|
-
version: "0.1.
|
|
88
|
+
version: "0.1.5",
|
|
62
89
|
});
|
|
63
90
|
// Registrace tools
|
|
64
91
|
registerCreateTicket(server, api);
|
|
@@ -107,6 +134,33 @@ async function main() {
|
|
|
107
134
|
// Phase 2 — Skupina F: Implementation Items
|
|
108
135
|
registerCreateImplementationItem(server, api);
|
|
109
136
|
registerDeleteImplementationItem(server, api);
|
|
137
|
+
// Phase 3 — Skupina A: Workflow & Status
|
|
138
|
+
registerUpdateTransitions(server, api);
|
|
139
|
+
registerUpdateStatus(server, api);
|
|
140
|
+
registerListWorkflowPresets(server, api);
|
|
141
|
+
registerGetNextStatuses(server, api);
|
|
142
|
+
// Phase 3 — Skupina B: Comments & Activity
|
|
143
|
+
registerUpdateComment(server, api);
|
|
144
|
+
registerDeleteComment(server, api);
|
|
145
|
+
// Phase 3 — Skupina C: Relations
|
|
146
|
+
registerListRelations(server, api);
|
|
147
|
+
// Phase 3 — Skupina D: Dev Queue
|
|
148
|
+
registerFailDevTask(server, api);
|
|
149
|
+
registerListDevAssignments(server, api);
|
|
150
|
+
// Phase 3 — Skupina E: Labels CRUD
|
|
151
|
+
registerCreateLabel(server, api);
|
|
152
|
+
registerUpdateLabel(server, api);
|
|
153
|
+
registerDeleteLabel(server, api);
|
|
154
|
+
// Phase 3 — Skupina F: Cycles/Epics extras
|
|
155
|
+
registerUpdateCycle(server, api);
|
|
156
|
+
registerDeleteCycle(server, api);
|
|
157
|
+
registerUpdateEpic(server, api);
|
|
158
|
+
registerDeleteEpic(server, api);
|
|
159
|
+
// ADR-0026 Phase 2f: Project Members
|
|
160
|
+
registerListProjectMembers(server, api);
|
|
161
|
+
registerAddProjectMember(server, api);
|
|
162
|
+
registerUpdateProjectMemberRole(server, api);
|
|
163
|
+
registerRemoveProjectMember(server, api);
|
|
110
164
|
// Registrace resources
|
|
111
165
|
registerTicketResource(server, api);
|
|
112
166
|
registerProjectResource(server, api);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerAddProjectMember(server, api) {
|
|
3
|
+
server.tool("add_project_member", "Add a member to a project with a specific role", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
user_id: z.string().describe("UUID of the user to add"),
|
|
6
|
+
role: z
|
|
7
|
+
.enum(["Viewer", "Developer", "Analytic", "Editor", "ProjectAdmin"])
|
|
8
|
+
.describe("Project role for the user"),
|
|
9
|
+
}, async (params) => {
|
|
10
|
+
try {
|
|
11
|
+
await api.post(`/api/projects/${params.project}/members`, {
|
|
12
|
+
userId: params.user_id,
|
|
13
|
+
role: params.role,
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: `Member added to project "${params.project}" with role ${params.role}`,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
27
|
+
isError: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=add-project-member.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerCreateLabel(server, api) {
|
|
3
|
+
server.tool("create_label", "Create a new label in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
name: z.string().describe("Label name"),
|
|
6
|
+
color: z.string().describe("Hex color code, e.g. '#ef4444'"),
|
|
7
|
+
}, async (params) => {
|
|
8
|
+
try {
|
|
9
|
+
const result = await api.post(`/api/projects/${params.project}/labels`, {
|
|
10
|
+
name: params.name,
|
|
11
|
+
color: params.color,
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: `Created label "${result.name}" (${result.id}) with color ${result.color}`,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=create-label.js.map
|
|
@@ -8,6 +8,7 @@ export function registerCreateTicket(server, api) {
|
|
|
8
8
|
priority: z.enum(["P0", "P1", "P2", "P3"]).default("P2"),
|
|
9
9
|
content: z.string().optional().describe("Markdown content (analysis, solution)"),
|
|
10
10
|
affected_parts: z.array(z.string()).optional(),
|
|
11
|
+
due_date: z.string().optional().describe("Due date in YYYY-MM-DD format"),
|
|
11
12
|
}, async (params) => {
|
|
12
13
|
try {
|
|
13
14
|
// Unescapovat double-escaped newlines z MCP tool parametrů
|
|
@@ -19,6 +20,7 @@ export function registerCreateTicket(server, api) {
|
|
|
19
20
|
priority: params.priority,
|
|
20
21
|
content,
|
|
21
22
|
affectedParts: params.affected_parts,
|
|
23
|
+
dueDate: params.due_date,
|
|
22
24
|
});
|
|
23
25
|
return {
|
|
24
26
|
content: [
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteComment(server, api) {
|
|
3
|
+
server.tool("delete_comment", "Delete a comment", {
|
|
4
|
+
comment_id: z.string().describe("UUID of the comment to delete"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
await api.delete(`/api/comments/${params.comment_id}`);
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{ type: "text", text: `Comment ${params.comment_id} deleted` },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
17
|
+
isError: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=delete-comment.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteCycle(server, api) {
|
|
3
|
+
server.tool("delete_cycle", "Delete a cycle (sprint) from a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
cycle_id: z.string().describe("UUID of the cycle to delete"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/projects/${params.project}/cycles/${params.cycle_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Cycle ${params.cycle_id} deleted from project ${params.project}`,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=delete-cycle.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteEpic(server, api) {
|
|
3
|
+
server.tool("delete_epic", "Delete an epic from a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
epic_id: z.string().describe("UUID of the epic to delete"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/projects/${params.project}/epics/${params.epic_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Epic ${params.epic_id} deleted from project ${params.project}`,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=delete-epic.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteLabel(server, api) {
|
|
3
|
+
server.tool("delete_label", "Delete a label from a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
label_id: z.string().describe("UUID of the label to delete"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/projects/${params.project}/labels/${params.label_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Label ${params.label_id} deleted from project ${params.project}`,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=delete-label.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerFailDevTask(server, api) {
|
|
3
|
+
server.tool("fail_dev_task", "Mark a dev agent task as failed with a reason", {
|
|
4
|
+
assignment_id: z.string().describe("UUID of the dev agent assignment"),
|
|
5
|
+
reason: z.string().describe("Reason why the task failed"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
const reason = params.reason.replace(/\\n/g, "\n");
|
|
9
|
+
await api.post(`/api/dev-queue/fail/${params.assignment_id}`, {
|
|
10
|
+
reason,
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: `Task ${params.assignment_id} marked as failed.`,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=fail-dev-task.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerGetNextStatuses(server, api) {
|
|
3
|
+
server.tool("get_next_statuses", "Get available next statuses for a ticket (based on current status and user role)", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' or 'TKR-BUG-0042'"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const statuses = await api.get(`/api/tickets/${params.number}/next-statuses`);
|
|
8
|
+
if (statuses.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: `No available transitions for ${params.number}` }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const lines = [
|
|
14
|
+
"| # | Status | Emoji | Color | Category |",
|
|
15
|
+
"|---|--------|-------|-------|----------|",
|
|
16
|
+
...statuses.map((s, i) => `| ${i + 1} | ${s.name} | ${s.emoji || "-"} | ${s.color} | ${s.category} |`),
|
|
17
|
+
];
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=get-next-statuses.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ApiClient } from "../api-client.js";
|
|
3
|
+
export declare function registerListDevAssignments(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=list-dev-assignments.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function registerListDevAssignments(server, api) {
|
|
2
|
+
server.tool("list_dev_assignments", "List all dev queue assignments (pending, in-progress, completed, failed)", {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const assignments = await api.get("/api/dev-queue/assignments");
|
|
5
|
+
if (assignments.length === 0) {
|
|
6
|
+
return {
|
|
7
|
+
content: [{ type: "text", text: "No dev assignments found." }],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const lines = [
|
|
11
|
+
"| # | Ticket | Title | Status | Agent | Assigned | Completed |",
|
|
12
|
+
"|---|--------|-------|--------|-------|----------|-----------|",
|
|
13
|
+
...assignments.map((a, i) => `| ${i + 1} | ${a.ticketDisplayNumber} | ${a.ticketTitle} | ${a.status} | ${a.agentName || "-"} | ${a.assignedAt} | ${a.completedAt || "-"} |`),
|
|
14
|
+
];
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
22
|
+
isError: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=list-dev-assignments.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ApiClient } from "../api-client.js";
|
|
3
|
+
export declare function registerListProjectMembers(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=list-project-members.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListProjectMembers(server, api) {
|
|
3
|
+
server.tool("list_project_members", "List members of a project with their roles", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const members = await api.get(`/api/projects/${params.project}/members`);
|
|
8
|
+
if (!members || members.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: "No members found." }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const header = "| User | Email | Role | Added |";
|
|
14
|
+
const separator = "|------|-------|------|-------|";
|
|
15
|
+
const rows = members.map((m) => `| ${m.userName} | ${m.email} | ${m.role} | ${m.addedAt?.substring(0, 10) ?? "—"} |`);
|
|
16
|
+
const text = [header, separator, ...rows].join("\n");
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text }],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=list-project-members.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListRelations(server, api) {
|
|
3
|
+
server.tool("list_relations", "List all relations for a ticket", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' or 'TKR-BUG-0042'"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const relations = await api.get(`/api/tickets/${params.number}/relations`);
|
|
8
|
+
if (relations.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: `No relations for ${params.number}` }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const lines = [
|
|
14
|
+
"| # | Direction | Type | Ticket | Title | Status |",
|
|
15
|
+
"|---|-----------|------|--------|-------|--------|",
|
|
16
|
+
...relations.map((r, i) => `| ${i + 1} | ${r.direction} | ${r.type} | ${r.displayNumber} | ${r.title} | ${r.status} |`),
|
|
17
|
+
];
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=list-relations.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ApiClient } from "../api-client.js";
|
|
3
|
+
export declare function registerListWorkflowPresets(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=list-workflow-presets.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function registerListWorkflowPresets(server, api) {
|
|
2
|
+
server.tool("list_workflow_presets", "List all available workflow presets", {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const presets = await api.get("/api/workflow-presets");
|
|
5
|
+
const lines = [
|
|
6
|
+
"| # | Name | Description | Statuses | Transitions |",
|
|
7
|
+
"|---|------|-------------|----------|-------------|",
|
|
8
|
+
...presets.map((p, i) => `| ${i + 1} | ${p.name} | ${p.description || "-"} | ${p.statusCount} | ${p.transitionCount} |`),
|
|
9
|
+
];
|
|
10
|
+
return {
|
|
11
|
+
content: [{ type: "text", text: lines.join("\n") || "No presets found." }],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
17
|
+
isError: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=list-workflow-presets.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ApiClient } from "../api-client.js";
|
|
3
|
+
export declare function registerRemoveProjectMember(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=remove-project-member.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerRemoveProjectMember(server, api) {
|
|
3
|
+
server.tool("remove_project_member", "Remove a member from a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
user_id: z.string().describe("UUID of the user to remove"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/projects/${params.project}/members/${params.user_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Member ${params.user_id} removed from project "${params.project}"`,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=remove-project-member.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateComment(server, api) {
|
|
3
|
+
server.tool("update_comment", "Update the text of an existing comment", {
|
|
4
|
+
comment_id: z.string().describe("UUID of the comment to update"),
|
|
5
|
+
text: z.string().describe("New comment text (markdown)"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
const text = params.text.replace(/\\n/g, "\n");
|
|
9
|
+
await api.put(`/api/comments/${params.comment_id}`, { text });
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{ type: "text", text: `Comment ${params.comment_id} updated` },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
19
|
+
isError: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=update-comment.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateCycle(server, api) {
|
|
3
|
+
server.tool("update_cycle", "Update a cycle (sprint) in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
cycle_id: z.string().describe("UUID of the cycle to update"),
|
|
6
|
+
name: z.string().optional().describe("New cycle name"),
|
|
7
|
+
startDate: z.string().optional().describe("New start date (ISO 8601, e.g. '2026-03-15')"),
|
|
8
|
+
endDate: z.string().optional().describe("New end date (ISO 8601, e.g. '2026-03-29')"),
|
|
9
|
+
status: z.string().optional().describe("New status: planned, active, completed"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const body = {};
|
|
13
|
+
if (params.name !== undefined)
|
|
14
|
+
body.name = params.name;
|
|
15
|
+
if (params.startDate !== undefined)
|
|
16
|
+
body.startDate = params.startDate;
|
|
17
|
+
if (params.endDate !== undefined)
|
|
18
|
+
body.endDate = params.endDate;
|
|
19
|
+
if (params.status !== undefined)
|
|
20
|
+
body.status = params.status;
|
|
21
|
+
await api.put(`/api/projects/${params.project}/cycles/${params.cycle_id}`, body);
|
|
22
|
+
return {
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: `Cycle ${params.cycle_id} updated in project ${params.project}`,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
34
|
+
isError: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=update-cycle.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateEpic(server, api) {
|
|
3
|
+
server.tool("update_epic", "Update an epic in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
epic_id: z.string().describe("UUID of the epic to update"),
|
|
6
|
+
name: z.string().optional().describe("New epic name"),
|
|
7
|
+
description: z.string().optional().describe("New epic description"),
|
|
8
|
+
color: z.string().optional().describe("New hex color code, e.g. '#6366f1'"),
|
|
9
|
+
startDate: z.string().optional().describe("New start date (YYYY-MM-DD)"),
|
|
10
|
+
targetDate: z.string().optional().describe("New target date (YYYY-MM-DD)"),
|
|
11
|
+
}, async (params) => {
|
|
12
|
+
try {
|
|
13
|
+
const body = {};
|
|
14
|
+
if (params.name !== undefined)
|
|
15
|
+
body.name = params.name;
|
|
16
|
+
if (params.description !== undefined)
|
|
17
|
+
body.description = params.description.replace(/\\n/g, "\n");
|
|
18
|
+
if (params.color !== undefined)
|
|
19
|
+
body.color = params.color;
|
|
20
|
+
if (params.startDate !== undefined)
|
|
21
|
+
body.startDate = params.startDate;
|
|
22
|
+
if (params.targetDate !== undefined)
|
|
23
|
+
body.targetDate = params.targetDate;
|
|
24
|
+
await api.put(`/api/projects/${params.project}/epics/${params.epic_id}`, body);
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: `Epic ${params.epic_id} updated in project ${params.project}`,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=update-epic.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateLabel(server, api) {
|
|
3
|
+
server.tool("update_label", "Update a label in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
label_id: z.string().describe("UUID of the label to update"),
|
|
6
|
+
name: z.string().optional().describe("New label name"),
|
|
7
|
+
color: z.string().optional().describe("New hex color code, e.g. '#ef4444'"),
|
|
8
|
+
}, async (params) => {
|
|
9
|
+
try {
|
|
10
|
+
const body = {};
|
|
11
|
+
if (params.name !== undefined)
|
|
12
|
+
body.name = params.name;
|
|
13
|
+
if (params.color !== undefined)
|
|
14
|
+
body.color = params.color;
|
|
15
|
+
await api.put(`/api/projects/${params.project}/labels/${params.label_id}`, body);
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: `Label ${params.label_id} updated in project ${params.project}`,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=update-label.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ApiClient } from "../api-client.js";
|
|
3
|
+
export declare function registerUpdateProjectMemberRole(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=update-project-member-role.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateProjectMemberRole(server, api) {
|
|
3
|
+
server.tool("update_project_member_role", "Update the role of a project member", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
user_id: z.string().describe("UUID of the user"),
|
|
6
|
+
role: z
|
|
7
|
+
.enum(["Viewer", "Developer", "Analytic", "Editor", "ProjectAdmin"])
|
|
8
|
+
.describe("New project role"),
|
|
9
|
+
}, async (params) => {
|
|
10
|
+
try {
|
|
11
|
+
await api.put(`/api/projects/${params.project}/members/${params.user_id}/role`, { role: params.role });
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: `Role updated to ${params.role} for user ${params.user_id} in project "${params.project}"`,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=update-project-member-role.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateStatus(server, api) {
|
|
3
|
+
server.tool("update_status", "Update a workflow status in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
status_id: z.string().describe("Status ID to update"),
|
|
6
|
+
name: z.string().optional().describe("New display name"),
|
|
7
|
+
emoji: z.string().optional().describe("New emoji icon"),
|
|
8
|
+
color: z.string().optional().describe("New hex color code, e.g. '#3b82f6'"),
|
|
9
|
+
category: z.string().optional().describe("New category: backlog, active, done, cancelled"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const body = {};
|
|
13
|
+
if (params.name !== undefined)
|
|
14
|
+
body.name = params.name;
|
|
15
|
+
if (params.emoji !== undefined)
|
|
16
|
+
body.emoji = params.emoji;
|
|
17
|
+
if (params.color !== undefined)
|
|
18
|
+
body.color = params.color;
|
|
19
|
+
if (params.category !== undefined)
|
|
20
|
+
body.category = params.category;
|
|
21
|
+
await api.put(`/api/projects/${params.project}/statuses/${params.status_id}`, body);
|
|
22
|
+
return {
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: `Status ${params.status_id} updated in project ${params.project}`,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
34
|
+
isError: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=update-status.js.map
|
|
@@ -8,13 +8,21 @@ export function registerUpdateTicket(server, api) {
|
|
|
8
8
|
priority: z.string().optional(),
|
|
9
9
|
assignee: z.string().optional().describe("Username or 'unassigned'"),
|
|
10
10
|
scope: z.string().optional(),
|
|
11
|
+
due_date: z.string().optional().describe("Due date in YYYY-MM-DD format, or 'clear' to remove"),
|
|
11
12
|
}, async (params) => {
|
|
12
13
|
try {
|
|
13
|
-
const { number, ...
|
|
14
|
+
const { number, due_date, ...rest } = params;
|
|
14
15
|
// Unescapovat double-escaped newlines z MCP tool parametrů
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
const
|
|
16
|
+
if (rest.content)
|
|
17
|
+
rest.content = rest.content.replace(/\\n/g, "\n");
|
|
18
|
+
const body = { ...rest };
|
|
19
|
+
if (due_date === "clear") {
|
|
20
|
+
body.clearDueDate = true;
|
|
21
|
+
}
|
|
22
|
+
else if (due_date) {
|
|
23
|
+
body.dueDate = due_date;
|
|
24
|
+
}
|
|
25
|
+
const ticket = await api.put(`/api/tickets/${number}`, body);
|
|
18
26
|
return {
|
|
19
27
|
content: [
|
|
20
28
|
{
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUpdateTransitions(server, api) {
|
|
3
|
+
server.tool("update_transitions", "Replace all workflow transitions for a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
transitions: z
|
|
6
|
+
.array(z.object({
|
|
7
|
+
fromStatusId: z.string().describe("Source status ID"),
|
|
8
|
+
toStatusId: z.string().describe("Target status ID"),
|
|
9
|
+
allowedRoles: z
|
|
10
|
+
.array(z.string())
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Roles allowed to perform this transition"),
|
|
13
|
+
}))
|
|
14
|
+
.describe("Array of transition definitions"),
|
|
15
|
+
}, async (params) => {
|
|
16
|
+
try {
|
|
17
|
+
await api.put(`/api/projects/${params.project}/transitions`, {
|
|
18
|
+
transitions: params.transitions,
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: `Updated ${params.transitions.length} transitions for project ${params.project}`,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
32
|
+
isError: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=update-transitions.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k-system/tickr-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server for Tickr project management — 40 tools for ticket CRUD, workflow, projects, attachments, triage, labels, cycles, epics",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|