@k-system/tickr-mcp 0.1.5 → 0.2.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/formatters.js +1 -0
- package/dist/index.js +48 -0
- package/dist/tools/apply-preset.d.ts +4 -0
- package/dist/tools/apply-preset.js +26 -0
- package/dist/tools/complete-dev-task.js +3 -1
- package/dist/tools/create-cycle.d.ts +4 -0
- package/dist/tools/create-cycle.js +32 -0
- package/dist/tools/create-epic.d.ts +4 -0
- package/dist/tools/create-epic.js +36 -0
- package/dist/tools/create-implementation-item.d.ts +4 -0
- package/dist/tools/create-implementation-item.js +32 -0
- package/dist/tools/create-project.d.ts +4 -0
- package/dist/tools/create-project.js +33 -0
- package/dist/tools/create-status.d.ts +4 -0
- package/dist/tools/create-status.js +42 -0
- package/dist/tools/delete-attachment.d.ts +4 -0
- package/dist/tools/delete-attachment.js +26 -0
- package/dist/tools/delete-implementation-item.d.ts +4 -0
- package/dist/tools/delete-implementation-item.js +26 -0
- package/dist/tools/delete-status.d.ts +4 -0
- package/dist/tools/delete-status.js +26 -0
- package/dist/tools/delete-ticket.d.ts +4 -0
- package/dist/tools/delete-ticket.js +25 -0
- package/dist/tools/get-board.d.ts +4 -0
- package/dist/tools/get-board.js +37 -0
- package/dist/tools/get-my-tasks.d.ts +4 -0
- package/dist/tools/get-my-tasks.js +20 -0
- package/dist/tools/get-project.d.ts +4 -0
- package/dist/tools/get-project.js +20 -0
- package/dist/tools/list-attachments.d.ts +4 -0
- package/dist/tools/list-attachments.js +37 -0
- package/dist/tools/list-comments.d.ts +4 -0
- package/dist/tools/list-comments.js +31 -0
- package/dist/tools/list-projects.d.ts +4 -0
- package/dist/tools/list-projects.js +22 -0
- package/dist/tools/list-statuses.d.ts +4 -0
- package/dist/tools/list-statuses.js +25 -0
- package/dist/tools/list-transitions.d.ts +4 -0
- package/dist/tools/list-transitions.js +25 -0
- package/dist/tools/update-implementation-item.js +1 -1
- package/package.json +2 -2
package/dist/formatters.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -26,6 +26,30 @@ import { registerCompleteDevTask } from "./tools/complete-dev-task.js";
|
|
|
26
26
|
import { registerWhoami } from "./tools/whoami.js";
|
|
27
27
|
import { registerAddRelation } from "./tools/add-relation.js";
|
|
28
28
|
import { registerRemoveRelation } from "./tools/remove-relation.js";
|
|
29
|
+
// Phase 2 — Skupina A: Workflow & Status Management
|
|
30
|
+
import { registerListStatuses } from "./tools/list-statuses.js";
|
|
31
|
+
import { registerListTransitions } from "./tools/list-transitions.js";
|
|
32
|
+
import { registerCreateStatus } from "./tools/create-status.js";
|
|
33
|
+
import { registerDeleteStatus } from "./tools/delete-status.js";
|
|
34
|
+
import { registerApplyPreset } from "./tools/apply-preset.js";
|
|
35
|
+
// Phase 2 — Skupina B: Project Management
|
|
36
|
+
import { registerListProjects } from "./tools/list-projects.js";
|
|
37
|
+
import { registerGetProject } from "./tools/get-project.js";
|
|
38
|
+
import { registerCreateProject } from "./tools/create-project.js";
|
|
39
|
+
// Phase 2 — Skupina C: Ticket Operations
|
|
40
|
+
import { registerDeleteTicket } from "./tools/delete-ticket.js";
|
|
41
|
+
import { registerGetBoard } from "./tools/get-board.js";
|
|
42
|
+
import { registerGetMyTasks } from "./tools/get-my-tasks.js";
|
|
43
|
+
import { registerListComments } from "./tools/list-comments.js";
|
|
44
|
+
// Phase 2 — Skupina D: Attachments
|
|
45
|
+
import { registerListAttachments } from "./tools/list-attachments.js";
|
|
46
|
+
import { registerDeleteAttachment } from "./tools/delete-attachment.js";
|
|
47
|
+
// Phase 2 — Skupina E: Cycles & Epics CRUD
|
|
48
|
+
import { registerCreateCycle } from "./tools/create-cycle.js";
|
|
49
|
+
import { registerCreateEpic } from "./tools/create-epic.js";
|
|
50
|
+
// Phase 2 — Skupina F: Implementation Items
|
|
51
|
+
import { registerCreateImplementationItem } from "./tools/create-implementation-item.js";
|
|
52
|
+
import { registerDeleteImplementationItem } from "./tools/delete-implementation-item.js";
|
|
29
53
|
// Resources
|
|
30
54
|
import { registerTicketResource } from "./resources/ticket-resource.js";
|
|
31
55
|
import { registerProjectResource } from "./resources/project-resource.js";
|
|
@@ -59,6 +83,30 @@ async function main() {
|
|
|
59
83
|
registerWhoami(server, api);
|
|
60
84
|
registerAddRelation(server, api);
|
|
61
85
|
registerRemoveRelation(server, api);
|
|
86
|
+
// Phase 2 — Skupina A: Workflow & Status Management
|
|
87
|
+
registerListStatuses(server, api);
|
|
88
|
+
registerListTransitions(server, api);
|
|
89
|
+
registerCreateStatus(server, api);
|
|
90
|
+
registerDeleteStatus(server, api);
|
|
91
|
+
registerApplyPreset(server, api);
|
|
92
|
+
// Phase 2 — Skupina B: Project Management
|
|
93
|
+
registerListProjects(server, api);
|
|
94
|
+
registerGetProject(server, api);
|
|
95
|
+
registerCreateProject(server, api);
|
|
96
|
+
// Phase 2 — Skupina C: Ticket Operations
|
|
97
|
+
registerDeleteTicket(server, api);
|
|
98
|
+
registerGetBoard(server, api);
|
|
99
|
+
registerGetMyTasks(server, api);
|
|
100
|
+
registerListComments(server, api);
|
|
101
|
+
// Phase 2 — Skupina D: Attachments
|
|
102
|
+
registerListAttachments(server, api);
|
|
103
|
+
registerDeleteAttachment(server, api);
|
|
104
|
+
// Phase 2 — Skupina E: Cycles & Epics CRUD
|
|
105
|
+
registerCreateCycle(server, api);
|
|
106
|
+
registerCreateEpic(server, api);
|
|
107
|
+
// Phase 2 — Skupina F: Implementation Items
|
|
108
|
+
registerCreateImplementationItem(server, api);
|
|
109
|
+
registerDeleteImplementationItem(server, api);
|
|
62
110
|
// Registrace resources
|
|
63
111
|
registerTicketResource(server, api);
|
|
64
112
|
registerProjectResource(server, api);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerApplyPreset(server, api) {
|
|
3
|
+
server.tool("apply_preset", "Apply a workflow preset to a project (replaces current statuses and transitions)", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
preset_id: z.string().describe("Preset ID to apply"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.post(`/api/projects/${params.project}/apply-preset`, { presetId: params.preset_id });
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Preset "${params.preset_id}" applied to 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=apply-preset.js.map
|
|
@@ -5,12 +5,14 @@ export function registerCompleteDevTask(server, api) {
|
|
|
5
5
|
summary: z.string().describe("Summary of what was done"),
|
|
6
6
|
branch: z.string().optional().describe("Git branch name"),
|
|
7
7
|
commit_hash: z.string().optional().describe("Last commit SHA"),
|
|
8
|
+
files_changed: z.array(z.string()).optional().describe("List of changed file paths"),
|
|
8
9
|
}, async (params) => {
|
|
9
10
|
try {
|
|
10
11
|
await api.post(`/api/dev-queue/complete/${params.assignment_id}`, {
|
|
11
|
-
resultSummary: params.summary,
|
|
12
|
+
resultSummary: params.summary.replace(/\\n/g, "\n"),
|
|
12
13
|
branchName: params.branch ?? null,
|
|
13
14
|
commitHash: params.commit_hash ?? null,
|
|
15
|
+
filesChanged: params.files_changed ?? null,
|
|
14
16
|
});
|
|
15
17
|
return {
|
|
16
18
|
content: [
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerCreateCycle(server, api) {
|
|
3
|
+
server.tool("create_cycle", "Create a new cycle (sprint) in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
name: z.string().describe("Cycle name"),
|
|
6
|
+
startDate: z.string().describe("Start date (ISO 8601, e.g. '2026-03-15')"),
|
|
7
|
+
endDate: z.string().describe("End date (ISO 8601, e.g. '2026-03-29')"),
|
|
8
|
+
}, async (params) => {
|
|
9
|
+
try {
|
|
10
|
+
const result = await api.post(`/api/projects/${params.project}/cycles`, {
|
|
11
|
+
name: params.name,
|
|
12
|
+
startDate: params.startDate,
|
|
13
|
+
endDate: params.endDate,
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: `Created cycle #${result.number}${result.name ? ` "${result.name}"` : ""} (${result.id})`,
|
|
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=create-cycle.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerCreateEpic(server, api) {
|
|
3
|
+
server.tool("create_epic", "Create a new epic in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
name: z.string().describe("Epic name"),
|
|
6
|
+
description: z.string().optional().describe("Epic description"),
|
|
7
|
+
color: z.string().optional().describe("Epic color hex code (e.g. #6366f1)"),
|
|
8
|
+
start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
9
|
+
target_date: z.string().optional().describe("Target date (YYYY-MM-DD)"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const result = await api.post(`/api/projects/${params.project}/epics`, {
|
|
13
|
+
name: params.name,
|
|
14
|
+
description: params.description,
|
|
15
|
+
color: params.color,
|
|
16
|
+
startDate: params.start_date,
|
|
17
|
+
targetDate: params.target_date,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: `Created epic "${result.name}" (${result.id})`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=create-epic.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 registerCreateImplementationItem(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=create-implementation-item.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerCreateImplementationItem(server, api) {
|
|
3
|
+
server.tool("create_implementation_item", "Add a new implementation item to a ticket", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
|
|
5
|
+
area: z.string().describe("Area: Database, API, Frontend, MCP, etc."),
|
|
6
|
+
fileOrDetail: z.string().describe("Specific file path or description of the work"),
|
|
7
|
+
note: z.string().optional().describe("Additional note"),
|
|
8
|
+
}, async (params) => {
|
|
9
|
+
try {
|
|
10
|
+
const result = await api.post(`/api/tickets/${params.number}/items`, {
|
|
11
|
+
area: params.area,
|
|
12
|
+
fileOrDetail: params.fileOrDetail,
|
|
13
|
+
note: params.note?.replace(/\\n/g, "\n"),
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: `Added implementation item "${result.area}: ${result.fileOrDetail}" to ${params.number}`,
|
|
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=create-implementation-item.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerCreateProject(server, api) {
|
|
3
|
+
server.tool("create_project", "Create a new project", {
|
|
4
|
+
name: z.string().describe("Project display name"),
|
|
5
|
+
slug: z.string().describe("URL-safe project slug (unique)"),
|
|
6
|
+
prefix: z.string().describe("Ticket prefix, e.g. 'VLX', 'TKR'"),
|
|
7
|
+
description: z.string().optional().describe("Project description"),
|
|
8
|
+
}, async (params) => {
|
|
9
|
+
try {
|
|
10
|
+
const result = await api.post("/api/projects", {
|
|
11
|
+
name: params.name,
|
|
12
|
+
slug: params.slug,
|
|
13
|
+
prefix: params.prefix,
|
|
14
|
+
description: params.description,
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: `Created project "${result.name}" (${result.slug})`,
|
|
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=create-project.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerCreateStatus(server, api) {
|
|
3
|
+
server.tool("create_status", "Create a new workflow status in a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
name: z.string().describe("Status display name"),
|
|
6
|
+
slug: z.string().describe("Status slug (URL-safe identifier)"),
|
|
7
|
+
emoji: z.string().optional().describe("Emoji icon for the status"),
|
|
8
|
+
color: z.string().describe("Hex color code, e.g. '#3b82f6'"),
|
|
9
|
+
category: z.string().describe("Category: backlog, active, done, cancelled"),
|
|
10
|
+
sortOrder: z.number().describe("Sort order (0-based)"),
|
|
11
|
+
isDefault: z.boolean().optional().default(false).describe("Whether this is the default status for new tickets"),
|
|
12
|
+
isTerminal: z.boolean().optional().default(false).describe("Whether this is a terminal (closed) status"),
|
|
13
|
+
}, async (params) => {
|
|
14
|
+
try {
|
|
15
|
+
const result = await api.post(`/api/projects/${params.project}/statuses`, {
|
|
16
|
+
name: params.name,
|
|
17
|
+
slug: params.slug,
|
|
18
|
+
emoji: params.emoji,
|
|
19
|
+
color: params.color,
|
|
20
|
+
category: params.category,
|
|
21
|
+
sortOrder: params.sortOrder,
|
|
22
|
+
isDefault: params.isDefault,
|
|
23
|
+
isTerminal: params.isTerminal,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: `Created status "${result.name}" (${result.id})`,
|
|
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=create-status.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteAttachment(server, api) {
|
|
3
|
+
server.tool("delete_attachment", "Delete an attachment from a ticket", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
|
|
5
|
+
attachment_id: z.string().describe("Attachment ID to delete"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/tickets/${params.number}/attachments/${params.attachment_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Attachment ${params.attachment_id} deleted from ${params.number}`,
|
|
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-attachment.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 registerDeleteImplementationItem(server: McpServer, api: ApiClient): void;
|
|
4
|
+
//# sourceMappingURL=delete-implementation-item.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteImplementationItem(server, api) {
|
|
3
|
+
server.tool("delete_implementation_item", "Delete an implementation item from a ticket", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
|
|
5
|
+
item_id: z.string().describe("Implementation item ID to delete"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/tickets/${params.number}/items/${params.item_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Implementation item ${params.item_id} deleted from ${params.number}`,
|
|
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-implementation-item.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteStatus(server, api) {
|
|
3
|
+
server.tool("delete_status", "Delete a workflow status from a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
status_id: z.string().describe("Status ID to delete"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
await api.delete(`/api/projects/${params.project}/statuses/${params.status_id}`);
|
|
9
|
+
return {
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: `Status ${params.status_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-status.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDeleteTicket(server, api) {
|
|
3
|
+
server.tool("delete_ticket", "Delete a ticket by display number", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
await api.delete(`/api/tickets/${params.number}`);
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: "text",
|
|
12
|
+
text: `Ticket ${params.number} deleted`,
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=delete-ticket.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerGetBoard(server, api) {
|
|
3
|
+
server.tool("get_board", "Get kanban board view for a project (tickets grouped by status columns)", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const columns = await api.get(`/api/projects/${params.project}/board`);
|
|
8
|
+
const lines = [];
|
|
9
|
+
for (const col of columns) {
|
|
10
|
+
const emoji = col.statusEmoji ? `${col.statusEmoji} ` : "";
|
|
11
|
+
lines.push(`## ${emoji}${col.status} (${col.tickets.length})`);
|
|
12
|
+
lines.push("");
|
|
13
|
+
if (col.tickets.length === 0) {
|
|
14
|
+
lines.push("_No tickets_");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
lines.push("| Ticket | Title | Priority | Assignee | Scope |");
|
|
18
|
+
lines.push("|--------|-------|----------|----------|-------|");
|
|
19
|
+
for (const t of col.tickets) {
|
|
20
|
+
lines.push(`| ${t.displayNumber} | ${t.title} | ${t.priority} | ${t.assigneeName || "-"} | ${t.scope || "-"} |`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
lines.push("");
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: lines.join("\n") || "Board is empty." }],
|
|
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=get-board.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function registerGetMyTasks(server, api) {
|
|
2
|
+
server.tool("get_my_tasks", "Get tickets assigned to the current user across all projects", {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const tickets = await api.get("/api/my-tasks");
|
|
5
|
+
const text = tickets
|
|
6
|
+
.map((t) => `${t.displayNumber} [${t.status}] ${t.title} (${t.priority}, ${t.scope || "-"})`)
|
|
7
|
+
.join("\n");
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: "text", text: text || "No tasks assigned to you." }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
15
|
+
isError: true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=get-my-tasks.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatProjectMarkdown } from "../formatters.js";
|
|
3
|
+
export function registerGetProject(server, api) {
|
|
4
|
+
server.tool("get_project", "Get project detail by slug", {
|
|
5
|
+
slug: z.string().describe("Project slug, e.g. 'tickr'"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
const project = await api.get(`/api/projects/${params.slug}`);
|
|
9
|
+
const md = formatProjectMarkdown(project);
|
|
10
|
+
return { content: [{ type: "text", text: md }] };
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
15
|
+
isError: true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=get-project.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListAttachments(server, api) {
|
|
3
|
+
server.tool("list_attachments", "List attachments on a ticket", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const attachments = await api.get(`/api/tickets/${params.number}/attachments`);
|
|
8
|
+
if (attachments.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: "No attachments on this ticket." }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const lines = [
|
|
14
|
+
"| # | File | Type | Size | Uploaded By | Date |",
|
|
15
|
+
"|---|------|------|------|-------------|------|",
|
|
16
|
+
...attachments.map((a, i) => `| ${i + 1} | ${a.fileName} | ${a.contentType} | ${formatSize(a.size)} | ${a.createdByName || "-"} | ${a.createdAt} |`),
|
|
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
|
+
function formatSize(bytes) {
|
|
31
|
+
if (bytes < 1024)
|
|
32
|
+
return `${bytes} B`;
|
|
33
|
+
if (bytes < 1024 * 1024)
|
|
34
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
35
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=list-attachments.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListComments(server, api) {
|
|
3
|
+
server.tool("list_comments", "List comments on a ticket", {
|
|
4
|
+
number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const comments = await api.get(`/api/tickets/${params.number}/comments`);
|
|
8
|
+
if (comments.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: "No comments on this ticket." }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const lines = [];
|
|
14
|
+
for (const c of comments) {
|
|
15
|
+
lines.push(`**${c.authorName}** (${c.createdAt}):`);
|
|
16
|
+
lines.push(c.text);
|
|
17
|
+
lines.push("");
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
26
|
+
isError: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=list-comments.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function registerListProjects(server, api) {
|
|
2
|
+
server.tool("list_projects", "List all projects accessible to the current user", {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const projects = await api.get("/api/projects");
|
|
5
|
+
const lines = [
|
|
6
|
+
"| Name | Slug | Prefix | Scope Tags | Ticket Types |",
|
|
7
|
+
"|------|------|--------|------------|--------------|",
|
|
8
|
+
...projects.map((p) => `| ${p.name} | ${p.slug} | ${p.prefix} | ${p.scopeTags?.join(", ") || "-"} | ${p.ticketTypes?.join(", ") || "-"} |`),
|
|
9
|
+
];
|
|
10
|
+
return {
|
|
11
|
+
content: [{ type: "text", text: lines.join("\n") || "No projects 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-projects.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListStatuses(server, api) {
|
|
3
|
+
server.tool("list_statuses", "List workflow statuses for a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const statuses = await api.get(`/api/projects/${params.project}/statuses`);
|
|
8
|
+
const lines = [
|
|
9
|
+
"| # | Name | Slug | Emoji | Color | Category | Default | Terminal |",
|
|
10
|
+
"|---|------|------|-------|-------|----------|---------|----------|",
|
|
11
|
+
...statuses.map((s, i) => `| ${i + 1} | ${s.name} | ${s.slug} | ${s.emoji || "-"} | ${s.color} | ${s.category} | ${s.isDefault ? "Yes" : "No"} | ${s.isTerminal ? "Yes" : "No"} |`),
|
|
12
|
+
];
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: lines.join("\n") || "No statuses found." }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=list-statuses.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListTransitions(server, api) {
|
|
3
|
+
server.tool("list_transitions", "List workflow transitions for a project", {
|
|
4
|
+
project: z.string().describe("Project slug"),
|
|
5
|
+
}, async (params) => {
|
|
6
|
+
try {
|
|
7
|
+
const transitions = await api.get(`/api/projects/${params.project}/transitions`);
|
|
8
|
+
const lines = [
|
|
9
|
+
"| # | From | To | Allowed Roles |",
|
|
10
|
+
"|---|------|----|---------------|",
|
|
11
|
+
...transitions.map((t, i) => `| ${i + 1} | ${t.fromStatusName} | ${t.toStatusName} | ${t.allowedRoles?.join(", ") || "-"} |`),
|
|
12
|
+
];
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: lines.join("\n") || "No transitions found." }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=list-transitions.js.map
|
|
@@ -11,7 +11,7 @@ export function registerUpdateImplementationItem(server, api) {
|
|
|
11
11
|
try {
|
|
12
12
|
await api.patch(`/api/tickets/${params.number}/items/${params.item_index}`, {
|
|
13
13
|
status: params.status,
|
|
14
|
-
note: params.note,
|
|
14
|
+
note: params.note?.replace(/\\n/g, "\n"),
|
|
15
15
|
});
|
|
16
16
|
return {
|
|
17
17
|
content: [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k-system/tickr-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Tickr project management —
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|
|
7
7
|
"bin": {
|