@k-system/tickr-mcp 0.4.0 → 0.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/dist/server.js CHANGED
@@ -76,6 +76,15 @@ import { registerListProjectMembers } from "./tools/list-project-members.js";
76
76
  import { registerAddProjectMember } from "./tools/add-project-member.js";
77
77
  import { registerUpdateProjectMemberRole } from "./tools/update-project-member-role.js";
78
78
  import { registerRemoveProjectMember } from "./tools/remove-project-member.js";
79
+ // ADR-0030: Releases
80
+ import { registerCreateRelease } from "./tools/create-release.js";
81
+ import { registerGetRelease } from "./tools/get-release.js";
82
+ import { registerListReleases } from "./tools/list-releases.js";
83
+ import { registerUpdateRelease } from "./tools/update-release.js";
84
+ import { registerPublishRelease } from "./tools/publish-release.js";
85
+ import { registerAddReleaseTicket } from "./tools/add-release-ticket.js";
86
+ import { registerRemoveReleaseTicket } from "./tools/remove-release-ticket.js";
87
+ import { registerGenerateReleaseNotes } from "./tools/generate-release-notes.js";
79
88
  // Resources
80
89
  import { registerTicketResource } from "./resources/ticket-resource.js";
81
90
  import { registerProjectResource } from "./resources/project-resource.js";
@@ -160,6 +169,15 @@ export async function startServer() {
160
169
  registerAddProjectMember(server, api);
161
170
  registerUpdateProjectMemberRole(server, api);
162
171
  registerRemoveProjectMember(server, api);
172
+ // ADR-0030: Releases
173
+ registerCreateRelease(server, api);
174
+ registerGetRelease(server, api);
175
+ registerListReleases(server, api);
176
+ registerUpdateRelease(server, api);
177
+ registerPublishRelease(server, api);
178
+ registerAddReleaseTicket(server, api);
179
+ registerRemoveReleaseTicket(server, api);
180
+ registerGenerateReleaseNotes(server, api);
163
181
  // Registrace resources
164
182
  registerTicketResource(server, api);
165
183
  registerProjectResource(server, api);
@@ -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 registerAddReleaseTicket(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=add-release-ticket.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ export function registerAddReleaseTicket(server, api) {
3
+ server.tool("add_release_ticket", "Link a ticket to a release", {
4
+ project: z.string().describe("Project slug"),
5
+ release_id: z.string().describe("UUID of the release"),
6
+ ticket_number: z.string().describe("Ticket display number, e.g. 'TKR-42' or 'TKR-BUG-0042'"),
7
+ }, async (params) => {
8
+ try {
9
+ await api.post(`/api/projects/${params.project}/releases/${params.release_id}/tickets`, { ticketNumber: params.ticket_number });
10
+ return {
11
+ content: [
12
+ {
13
+ type: "text",
14
+ text: `Ticket ${params.ticket_number} added to release ${params.release_id}`,
15
+ },
16
+ ],
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=add-release-ticket.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 registerCreateRelease(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=create-release.d.ts.map
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ export function registerCreateRelease(server, api) {
3
+ server.tool("create_release", "Create a new release in a project", {
4
+ project: z.string().describe("Project slug, e.g. 'tickr'"),
5
+ version: z.string().describe("Semantic version, e.g. '1.6.0'"),
6
+ name: z.string().optional().describe("Human-readable release name"),
7
+ git_tag: z.string().optional().describe("Git tag name, e.g. 'v1.6.0'"),
8
+ cycle_id: z.string().optional().describe("UUID of linked cycle (sprint)"),
9
+ }, async (params) => {
10
+ try {
11
+ const result = await api.post(`/api/projects/${params.project}/releases`, {
12
+ version: params.version,
13
+ name: params.name,
14
+ gitTag: params.git_tag,
15
+ cycleId: params.cycle_id,
16
+ });
17
+ return {
18
+ content: [
19
+ {
20
+ type: "text",
21
+ text: `Created release ${result.version} (id: ${result.id})`,
22
+ },
23
+ ],
24
+ };
25
+ }
26
+ catch (err) {
27
+ return {
28
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
29
+ isError: true,
30
+ };
31
+ }
32
+ });
33
+ }
34
+ //# sourceMappingURL=create-release.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 registerGenerateReleaseNotes(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=generate-release-notes.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { z } from "zod";
2
+ export function registerGenerateReleaseNotes(server, api) {
3
+ server.tool("generate_release_notes", "Generate markdown release notes from linked tickets (client-side generation)", {
4
+ project: z.string().describe("Project slug"),
5
+ release_id: z.string().describe("UUID of the release"),
6
+ }, async (params) => {
7
+ try {
8
+ const r = await api.get(`/api/projects/${params.project}/releases/${params.release_id}`);
9
+ const lines = [
10
+ `# Release ${r.version}${r.name ? ` — ${r.name}` : ""}`,
11
+ "",
12
+ ];
13
+ if (!r.tickets?.length) {
14
+ lines.push("No tickets linked to this release.");
15
+ return {
16
+ content: [{ type: "text", text: lines.join("\n") }],
17
+ };
18
+ }
19
+ // Seskupit tickety podle typu
20
+ const grouped = {};
21
+ for (const t of r.tickets) {
22
+ const key = t.type.toLowerCase();
23
+ if (!grouped[key])
24
+ grouped[key] = [];
25
+ grouped[key].push(t);
26
+ }
27
+ // Pořadí sekcí a labely
28
+ const sectionOrder = [
29
+ ["feature", "Features"],
30
+ ["adr", "Architecture Decisions"],
31
+ ["bug", "Bug Fixes"],
32
+ ["task", "Tasks"],
33
+ ];
34
+ for (const [typeKey, sectionTitle] of sectionOrder) {
35
+ const tickets = grouped[typeKey];
36
+ if (!tickets?.length)
37
+ continue;
38
+ lines.push(`## ${sectionTitle}`, "");
39
+ for (const t of tickets) {
40
+ lines.push(`- **${t.displayNumber}** ${t.title}`);
41
+ }
42
+ lines.push("");
43
+ }
44
+ // Ostatní typy, které nejsou ve standardním pořadí
45
+ for (const [typeKey, tickets] of Object.entries(grouped)) {
46
+ if (sectionOrder.some(([k]) => k === typeKey))
47
+ continue;
48
+ lines.push(`## ${typeKey.charAt(0).toUpperCase() + typeKey.slice(1)}`, "");
49
+ for (const t of tickets) {
50
+ lines.push(`- **${t.displayNumber}** ${t.title}`);
51
+ }
52
+ lines.push("");
53
+ }
54
+ return {
55
+ content: [{ type: "text", text: lines.join("\n") }],
56
+ };
57
+ }
58
+ catch (err) {
59
+ return {
60
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
61
+ isError: true,
62
+ };
63
+ }
64
+ });
65
+ }
66
+ //# sourceMappingURL=generate-release-notes.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 registerGetRelease(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=get-release.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ export function registerGetRelease(server, api) {
3
+ server.tool("get_release", "Get full release detail including linked tickets", {
4
+ project: z.string().describe("Project slug"),
5
+ release_id: z.string().describe("UUID of the release"),
6
+ }, async (params) => {
7
+ try {
8
+ const r = await api.get(`/api/projects/${params.project}/releases/${params.release_id}`);
9
+ const lines = [
10
+ `# Release ${r.version}${r.name ? ` — ${r.name}` : ""}`,
11
+ "",
12
+ `| Field | Value |`,
13
+ `|-------|-------|`,
14
+ `| Id | ${r.id} |`,
15
+ `| Status | ${r.status} |`,
16
+ `| Git Tag | ${r.gitTag || "-"} |`,
17
+ `| Published | ${r.publishedAt || "-"} |`,
18
+ `| Created | ${r.createdAt} |`,
19
+ `| Updated | ${r.updatedAt} |`,
20
+ ];
21
+ if (r.notes) {
22
+ lines.push("", "## Notes", "", r.notes);
23
+ }
24
+ if (r.tickets?.length > 0) {
25
+ lines.push("", "## Tickets", "", "| Number | Type | Title | Status |", "|--------|------|-------|--------|");
26
+ for (const t of r.tickets) {
27
+ lines.push(`| ${t.displayNumber} | ${t.type} | ${t.title} | ${t.status} |`);
28
+ }
29
+ }
30
+ return {
31
+ content: [{ type: "text", text: lines.join("\n") }],
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=get-release.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 registerListReleases(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=list-releases.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ export function registerListReleases(server, api) {
3
+ server.tool("list_releases", "List releases in a project with optional status filter", {
4
+ project: z.string().describe("Project slug"),
5
+ status: z.string().optional().describe("Filter by status: draft, published, archived"),
6
+ }, async (params) => {
7
+ try {
8
+ const qs = new URLSearchParams();
9
+ if (params.status)
10
+ qs.set("status", params.status);
11
+ const qsStr = qs.toString();
12
+ const path = `/api/projects/${params.project}/releases${qsStr ? `?${qsStr}` : ""}`;
13
+ const releases = await api.get(path);
14
+ if (!releases.length) {
15
+ return {
16
+ content: [{ type: "text", text: "No releases found." }],
17
+ };
18
+ }
19
+ const lines = [
20
+ "| Version | Name | Status | Tag | Tickets | Published | Created |",
21
+ "|---------|------|--------|-----|---------|-----------|---------|",
22
+ ];
23
+ for (const r of releases) {
24
+ lines.push(`| ${r.version} | ${r.name || "-"} | ${r.status} | ${r.gitTag || "-"} | ${r.ticketCount} | ${r.publishedAt || "-"} | ${r.createdAt} |`);
25
+ }
26
+ return {
27
+ content: [{ type: "text", text: lines.join("\n") }],
28
+ };
29
+ }
30
+ catch (err) {
31
+ return {
32
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
33
+ isError: true,
34
+ };
35
+ }
36
+ });
37
+ }
38
+ //# sourceMappingURL=list-releases.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 registerPublishRelease(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=publish-release.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ export function registerPublishRelease(server, api) {
3
+ server.tool("publish_release", "Publish a draft release — sets status to published and records publishedAt timestamp", {
4
+ project: z.string().describe("Project slug"),
5
+ release_id: z.string().describe("UUID of the release to publish"),
6
+ }, async (params) => {
7
+ try {
8
+ await api.post(`/api/projects/${params.project}/releases/${params.release_id}/publish`);
9
+ return {
10
+ content: [
11
+ {
12
+ type: "text",
13
+ text: `Release ${params.release_id} published in 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=publish-release.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 registerRemoveReleaseTicket(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=remove-release-ticket.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ export function registerRemoveReleaseTicket(server, api) {
3
+ server.tool("remove_release_ticket", "Remove a ticket from a release", {
4
+ project: z.string().describe("Project slug"),
5
+ release_id: z.string().describe("UUID of the release"),
6
+ ticket_id: z.string().describe("UUID of the ticket to remove from the release"),
7
+ }, async (params) => {
8
+ try {
9
+ await api.delete(`/api/projects/${params.project}/releases/${params.release_id}/tickets/${params.ticket_id}`);
10
+ return {
11
+ content: [
12
+ {
13
+ type: "text",
14
+ text: `Ticket ${params.ticket_id} removed from release ${params.release_id}`,
15
+ },
16
+ ],
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=remove-release-ticket.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 registerUpdateRelease(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=update-release.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ export function registerUpdateRelease(server, api) {
3
+ server.tool("update_release", "Update a release (name, notes, status)", {
4
+ project: z.string().describe("Project slug"),
5
+ release_id: z.string().describe("UUID of the release to update"),
6
+ name: z.string().optional().describe("New release name"),
7
+ notes: z.string().optional().describe("Release notes (markdown)"),
8
+ status: z.string().optional().describe("New status: draft, published, archived"),
9
+ }, async (params) => {
10
+ try {
11
+ const body = {};
12
+ if (params.name !== undefined)
13
+ body.name = params.name;
14
+ if (params.notes !== undefined)
15
+ body.notes = params.notes?.replace(/\\n/g, "\n");
16
+ if (params.status !== undefined)
17
+ body.status = params.status;
18
+ await api.put(`/api/projects/${params.project}/releases/${params.release_id}`, body);
19
+ return {
20
+ content: [
21
+ {
22
+ type: "text",
23
+ text: `Release ${params.release_id} updated in project ${params.project}`,
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=update-release.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k-system/tickr-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server for Tickr project management — 56 tools + setup CLI wizard",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",