@k-system/tickr-mcp 0.1.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +87 -0
  3. package/dist/api-client.d.ts +29 -0
  4. package/dist/api-client.js +65 -0
  5. package/dist/auth.d.ts +18 -0
  6. package/dist/auth.js +60 -0
  7. package/dist/config.d.ts +8 -0
  8. package/dist/config.js +33 -0
  9. package/dist/formatters.d.ts +10 -0
  10. package/dist/formatters.js +58 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.js +67 -0
  13. package/dist/resources/project-resource.d.ts +4 -0
  14. package/dist/resources/project-resource.js +18 -0
  15. package/dist/resources/ticket-resource.d.ts +4 -0
  16. package/dist/resources/ticket-resource.js +17 -0
  17. package/dist/tools/add-comment.d.ts +4 -0
  18. package/dist/tools/add-comment.js +25 -0
  19. package/dist/tools/add-label.d.ts +4 -0
  20. package/dist/tools/add-label.js +21 -0
  21. package/dist/tools/assign-cycle.d.ts +4 -0
  22. package/dist/tools/assign-cycle.js +22 -0
  23. package/dist/tools/assign-epic.d.ts +4 -0
  24. package/dist/tools/assign-epic.js +22 -0
  25. package/dist/tools/complete-dev-task.d.ts +4 -0
  26. package/dist/tools/complete-dev-task.js +37 -0
  27. package/dist/tools/create-ticket.d.ts +4 -0
  28. package/dist/tools/create-ticket.js +38 -0
  29. package/dist/tools/get-ticket.d.ts +4 -0
  30. package/dist/tools/get-ticket.js +20 -0
  31. package/dist/tools/list-cycles.d.ts +4 -0
  32. package/dist/tools/list-cycles.js +23 -0
  33. package/dist/tools/list-epics.d.ts +4 -0
  34. package/dist/tools/list-epics.js +23 -0
  35. package/dist/tools/list-labels.d.ts +4 -0
  36. package/dist/tools/list-labels.js +23 -0
  37. package/dist/tools/list-tickets.d.ts +4 -0
  38. package/dist/tools/list-tickets.js +39 -0
  39. package/dist/tools/poll-dev-queue.d.ts +4 -0
  40. package/dist/tools/poll-dev-queue.js +37 -0
  41. package/dist/tools/remove-label.d.ts +4 -0
  42. package/dist/tools/remove-label.js +21 -0
  43. package/dist/tools/search-tickets.d.ts +4 -0
  44. package/dist/tools/search-tickets.js +30 -0
  45. package/dist/tools/triage-accept.d.ts +4 -0
  46. package/dist/tools/triage-accept.js +30 -0
  47. package/dist/tools/triage-list.d.ts +4 -0
  48. package/dist/tools/triage-list.js +23 -0
  49. package/dist/tools/triage-reject.d.ts +4 -0
  50. package/dist/tools/triage-reject.js +28 -0
  51. package/dist/tools/update-implementation-item.d.ts +4 -0
  52. package/dist/tools/update-implementation-item.js +33 -0
  53. package/dist/tools/update-ticket.d.ts +4 -0
  54. package/dist/tools/update-ticket.js +32 -0
  55. package/dist/types.d.ts +88 -0
  56. package/dist/types.js +3 -0
  57. package/package.json +55 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@tickr/mcp-server` will be documented here.
4
+
5
+ ## [0.1.0] — 2026-03-12
6
+
7
+ ### Added
8
+ - Initial release
9
+ - 17 MCP tools (ticket CRUD, triage, labels, cycles, epics)
10
+ - 2 MCP resources (ticket detail, project overview)
11
+ - API key and JWT authentication
12
+ - Config via environment variables or `~/.tickr/config.json`
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @tickr/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for [Tickr](https://tickr.io) project management.
4
+
5
+ ## Quick Start
6
+
7
+ ### Claude Desktop
8
+
9
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
10
+ or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
11
+
12
+ ```json
13
+ {
14
+ "mcpServers": {
15
+ "tickr": {
16
+ "command": "npx",
17
+ "args": ["-y", "@tickr/mcp-server"],
18
+ "env": {
19
+ "TICKR_API_URL": "https://your-tickr-instance.com",
20
+ "TICKR_API_KEY": "tk_your_api_key"
21
+ }
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ### Claude Code
28
+
29
+ ```bash
30
+ claude mcp add tickr -- npx -y @tickr/mcp-server
31
+ ```
32
+
33
+ Set environment variables:
34
+ ```bash
35
+ export TICKR_API_URL=https://your-tickr-instance.com
36
+ export TICKR_API_KEY=tk_your_api_key
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ | Variable | Required | Default | Description |
42
+ |----------|----------|---------|-------------|
43
+ | `TICKR_API_URL` | Yes | `https://localhost:6001` | Tickr API base URL |
44
+ | `TICKR_API_KEY` | Yes | — | API key (`tk_...`) from Tickr admin |
45
+ | `TICKR_DEFAULT_PROJECT` | No | — | Default project slug |
46
+
47
+ Or use config file `~/.tickr/config.json`:
48
+ ```json
49
+ {
50
+ "apiUrl": "https://your-tickr-instance.com",
51
+ "apiKey": "tk_your_api_key",
52
+ "defaultProject": "my-project"
53
+ }
54
+ ```
55
+
56
+ ## Available Tools (17)
57
+
58
+ | Tool | Description |
59
+ |------|-------------|
60
+ | `create_ticket` | Create a new ticket |
61
+ | `list_tickets` | List tickets with filters |
62
+ | `get_ticket` | Get ticket detail by number |
63
+ | `update_ticket` | Update ticket fields |
64
+ | `search_tickets` | Fulltext search |
65
+ | `add_comment` | Add comment to ticket |
66
+ | `update_implementation_item` | Update implementation table row |
67
+ | `triage_list` | List untriaged tickets |
68
+ | `triage_accept` | Accept triaged ticket |
69
+ | `triage_reject` | Reject triaged ticket |
70
+ | `list_labels` | List project labels |
71
+ | `add_label` | Add label to ticket |
72
+ | `remove_label` | Remove label from ticket |
73
+ | `list_cycles` | List project cycles |
74
+ | `assign_cycle` | Assign ticket to cycle |
75
+ | `list_epics` | List project epics |
76
+ | `assign_epic` | Assign ticket to epic |
77
+
78
+ ## Resources (2)
79
+
80
+ | URI | Description |
81
+ |-----|-------------|
82
+ | `tickr://ticket/{number}` | Ticket detail as markdown |
83
+ | `tickr://project/{slug}` | Project overview + recent tickets |
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,29 @@
1
+ import type { TickrConfig } from "./config.js";
2
+ /** Unified API response wrapper */
3
+ export interface ApiResponse<T> {
4
+ success: boolean;
5
+ data: T | null;
6
+ error: {
7
+ code: string;
8
+ message: string;
9
+ traceId?: string;
10
+ } | null;
11
+ meta: {
12
+ totalCount?: number;
13
+ page?: number;
14
+ pageSize?: number;
15
+ totalPages?: number;
16
+ } | null;
17
+ }
18
+ /** Fetch wrapper pro Tickr REST API s automatickou autentizací */
19
+ export declare class ApiClient {
20
+ private config;
21
+ constructor(config: TickrConfig);
22
+ get<T = unknown>(path: string): Promise<T>;
23
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
24
+ patch<T = unknown>(path: string, body?: unknown): Promise<T>;
25
+ put<T = unknown>(path: string, body?: unknown): Promise<T>;
26
+ delete<T = unknown>(path: string): Promise<T>;
27
+ private request;
28
+ }
29
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { getAuthHeader } from "./auth.js";
2
+ /** Fetch wrapper pro Tickr REST API s automatickou autentizací */
3
+ export class ApiClient {
4
+ config;
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ async get(path) {
9
+ return this.request("GET", path);
10
+ }
11
+ async post(path, body) {
12
+ return this.request("POST", path, body);
13
+ }
14
+ async patch(path, body) {
15
+ return this.request("PATCH", path, body);
16
+ }
17
+ async put(path, body) {
18
+ return this.request("PUT", path, body);
19
+ }
20
+ async delete(path) {
21
+ return this.request("DELETE", path);
22
+ }
23
+ async request(method, path, body) {
24
+ const auth = await getAuthHeader(this.config);
25
+ const url = `${this.config.apiUrl}${path}`;
26
+ const headers = {
27
+ Authorization: auth,
28
+ Accept: "application/json",
29
+ };
30
+ if (body !== undefined) {
31
+ headers["Content-Type"] = "application/json";
32
+ }
33
+ const res = await fetch(url, {
34
+ method,
35
+ headers,
36
+ body: body !== undefined ? JSON.stringify(body) : undefined,
37
+ });
38
+ if (!res.ok) {
39
+ // Pokus o parsování unified error response
40
+ const text = await res.text().catch(() => "");
41
+ let errorMessage = `API ${method} ${path} failed: ${res.status} ${res.statusText}`;
42
+ try {
43
+ const json = JSON.parse(text);
44
+ if (json.error?.message) {
45
+ errorMessage = json.error.message;
46
+ }
47
+ }
48
+ catch {
49
+ if (text)
50
+ errorMessage += ` — ${text}`;
51
+ }
52
+ throw new Error(errorMessage);
53
+ }
54
+ // 204 No Content
55
+ if (res.status === 204) {
56
+ return undefined;
57
+ }
58
+ const json = await res.json();
59
+ if (!json.success) {
60
+ throw new Error(json.error?.message ?? `API ${method} ${path} failed`);
61
+ }
62
+ return json.data;
63
+ }
64
+ }
65
+ //# sourceMappingURL=api-client.js.map
package/dist/auth.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { TickrConfig } from "./config.js";
2
+ interface TokenPair {
3
+ accessToken: string;
4
+ refreshToken: string;
5
+ expiresAt: number;
6
+ }
7
+ /** Přihlášení přes username/password — vrátí JWT token pair */
8
+ export declare function login(config: TickrConfig, username: string, password: string): Promise<TokenPair>;
9
+ /** Refresh JWT tokenu */
10
+ export declare function refreshToken(config: TickrConfig): Promise<TokenPair>;
11
+ /**
12
+ * Vrátí Authorization header value.
13
+ * Pokud config má apiKey (tk_...), použije ho přímo.
14
+ * Jinak použije JWT token (auto-refresh pokud expiruje).
15
+ */
16
+ export declare function getAuthHeader(config: TickrConfig): Promise<string>;
17
+ export {};
18
+ //# sourceMappingURL=auth.d.ts.map
package/dist/auth.js ADDED
@@ -0,0 +1,60 @@
1
+ let currentTokens = null;
2
+ /** Přihlášení přes username/password — vrátí JWT token pair */
3
+ export async function login(config, username, password) {
4
+ const res = await fetch(`${config.apiUrl}/api/auth/login`, {
5
+ method: "POST",
6
+ headers: { "Content-Type": "application/json" },
7
+ body: JSON.stringify({ username, password }),
8
+ });
9
+ if (!res.ok) {
10
+ throw new Error(`Login failed: ${res.status} ${res.statusText}`);
11
+ }
12
+ const data = (await res.json());
13
+ currentTokens = {
14
+ accessToken: data.accessToken,
15
+ refreshToken: data.refreshToken,
16
+ expiresAt: Date.now() + data.expiresIn * 1000,
17
+ };
18
+ return currentTokens;
19
+ }
20
+ /** Refresh JWT tokenu */
21
+ export async function refreshToken(config) {
22
+ if (!currentTokens) {
23
+ throw new Error("No tokens to refresh — login first");
24
+ }
25
+ const res = await fetch(`${config.apiUrl}/api/auth/refresh`, {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/json" },
28
+ body: JSON.stringify({ refreshToken: currentTokens.refreshToken }),
29
+ });
30
+ if (!res.ok) {
31
+ throw new Error(`Token refresh failed: ${res.status}`);
32
+ }
33
+ const data = (await res.json());
34
+ currentTokens = {
35
+ accessToken: data.accessToken,
36
+ refreshToken: data.refreshToken,
37
+ expiresAt: Date.now() + data.expiresIn * 1000,
38
+ };
39
+ return currentTokens;
40
+ }
41
+ /**
42
+ * Vrátí Authorization header value.
43
+ * Pokud config má apiKey (tk_...), použije ho přímo.
44
+ * Jinak použije JWT token (auto-refresh pokud expiruje).
45
+ */
46
+ export async function getAuthHeader(config) {
47
+ // API key auth — jednoduché, bez expiry
48
+ if (config.apiKey) {
49
+ return config.apiKey;
50
+ }
51
+ // JWT auth — refresh pokud token brzy vyprší (30s buffer)
52
+ if (currentTokens && currentTokens.expiresAt - Date.now() < 30_000) {
53
+ await refreshToken(config);
54
+ }
55
+ if (!currentTokens) {
56
+ throw new Error("Not authenticated — set TICKR_API_KEY or call login()");
57
+ }
58
+ return `Bearer ${currentTokens.accessToken}`;
59
+ }
60
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1,8 @@
1
+ export interface TickrConfig {
2
+ apiUrl: string;
3
+ apiKey?: string;
4
+ defaultProject?: string;
5
+ }
6
+ /** Načte konfiguraci z env vars nebo ~/.tickr/config.json */
7
+ export declare function loadConfig(): TickrConfig;
8
+ //# sourceMappingURL=config.d.ts.map
package/dist/config.js ADDED
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ /** Načte konfiguraci z env vars nebo ~/.tickr/config.json */
5
+ export function loadConfig() {
6
+ const apiUrl = process.env.TICKR_API_URL;
7
+ const apiKey = process.env.TICKR_API_KEY;
8
+ const defaultProject = process.env.TICKR_DEFAULT_PROJECT;
9
+ // Env vars mají prioritu
10
+ if (apiUrl && apiKey) {
11
+ return { apiUrl, apiKey, defaultProject };
12
+ }
13
+ // Fallback na config soubor
14
+ const configPath = join(homedir(), ".tickr", "config.json");
15
+ try {
16
+ const raw = readFileSync(configPath, "utf-8");
17
+ const file = JSON.parse(raw);
18
+ return {
19
+ apiUrl: apiUrl || file.apiUrl || "https://localhost:6001",
20
+ apiKey: apiKey || file.apiKey || "",
21
+ defaultProject: defaultProject || file.defaultProject,
22
+ };
23
+ }
24
+ catch {
25
+ // Config soubor neexistuje — použijeme defaults
26
+ return {
27
+ apiUrl: apiUrl || "https://localhost:6001",
28
+ apiKey: apiKey || "",
29
+ defaultProject,
30
+ };
31
+ }
32
+ }
33
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,10 @@
1
+ import type { Ticket, Project } from "./types.js";
2
+ /** Formátuje ticket do markdown pro resource/tool output */
3
+ export declare function formatTicketMarkdown(t: Ticket): string;
4
+ /** Formátuje projekt do markdown pro resource output */
5
+ export declare function formatProjectMarkdown(p: Project, recentTickets?: Array<{
6
+ displayNumber: string;
7
+ title: string;
8
+ status: string;
9
+ }>): string;
10
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1,58 @@
1
+ /** Formátuje ticket do markdown pro resource/tool output */
2
+ export function formatTicketMarkdown(t) {
3
+ const lines = [
4
+ `# ${t.displayNumber}: ${t.title}`,
5
+ "",
6
+ `| Field | Value |`,
7
+ `|-------|-------|`,
8
+ `| Type | ${t.type} |`,
9
+ `| Status | ${t.status} |`,
10
+ `| Priority | ${t.priority} |`,
11
+ `| Scope | ${t.scope || "-"} |`,
12
+ `| Assignee | ${t.assigneeName || "Unassigned"} |`,
13
+ `| Estimate | ${t.estimate || "-"} |`,
14
+ `| Created | ${t.createdAt} |`,
15
+ `| Updated | ${t.updatedAt} |`,
16
+ ];
17
+ if (t.affectedParts.length > 0) {
18
+ lines.push("", `**Affected parts:** ${t.affectedParts.join(", ")}`);
19
+ }
20
+ if (t.content) {
21
+ lines.push("", "---", "", t.content);
22
+ }
23
+ if (t.implementationItems.length > 0) {
24
+ lines.push("", "## Implementation Items", "", "| # | Area | Detail | Status | Note |", "|---|------|--------|--------|------|");
25
+ t.implementationItems.forEach((item, i) => {
26
+ lines.push(`| ${i} | ${item.area} | ${item.fileOrDetail} | ${item.status} | ${item.note || "-"} |`);
27
+ });
28
+ }
29
+ if (t.comments.length > 0) {
30
+ lines.push("", "## Comments", "");
31
+ for (const c of t.comments) {
32
+ lines.push(`**${c.authorName}** (${c.createdAt}):`);
33
+ lines.push(c.text, "");
34
+ }
35
+ }
36
+ return lines.join("\n");
37
+ }
38
+ /** Formátuje projekt do markdown pro resource output */
39
+ export function formatProjectMarkdown(p, recentTickets) {
40
+ const lines = [
41
+ `# Project: ${p.name} (${p.slug})`,
42
+ "",
43
+ `**Prefix:** ${p.prefix}`,
44
+ `**Scope tags:** ${p.scopeTags.join(", ") || "-"}`,
45
+ `**Ticket types:** ${p.ticketTypes.join(", ") || "-"}`,
46
+ ];
47
+ if (p.description) {
48
+ lines.push("", p.description);
49
+ }
50
+ if (recentTickets && recentTickets.length > 0) {
51
+ lines.push("", "## Recent Tickets", "");
52
+ for (const t of recentTickets) {
53
+ lines.push(`- ${t.displayNumber} [${t.status}] ${t.title}`);
54
+ }
55
+ }
56
+ return lines.join("\n");
57
+ }
58
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { loadConfig } from "./config.js";
5
+ import { ApiClient } from "./api-client.js";
6
+ // Tools
7
+ import { registerCreateTicket } from "./tools/create-ticket.js";
8
+ import { registerListTickets } from "./tools/list-tickets.js";
9
+ import { registerGetTicket } from "./tools/get-ticket.js";
10
+ import { registerUpdateTicket } from "./tools/update-ticket.js";
11
+ import { registerAddComment } from "./tools/add-comment.js";
12
+ import { registerUpdateImplementationItem } from "./tools/update-implementation-item.js";
13
+ import { registerSearchTickets } from "./tools/search-tickets.js";
14
+ import { registerTriageList } from "./tools/triage-list.js";
15
+ import { registerTriageAccept } from "./tools/triage-accept.js";
16
+ import { registerTriageReject } from "./tools/triage-reject.js";
17
+ import { registerListLabels } from "./tools/list-labels.js";
18
+ import { registerAddLabel } from "./tools/add-label.js";
19
+ import { registerRemoveLabel } from "./tools/remove-label.js";
20
+ import { registerListCycles } from "./tools/list-cycles.js";
21
+ import { registerAssignCycle } from "./tools/assign-cycle.js";
22
+ import { registerListEpics } from "./tools/list-epics.js";
23
+ import { registerAssignEpic } from "./tools/assign-epic.js";
24
+ import { registerPollDevQueue } from "./tools/poll-dev-queue.js";
25
+ import { registerCompleteDevTask } from "./tools/complete-dev-task.js";
26
+ // Resources
27
+ import { registerTicketResource } from "./resources/ticket-resource.js";
28
+ import { registerProjectResource } from "./resources/project-resource.js";
29
+ async function main() {
30
+ const config = loadConfig();
31
+ const api = new ApiClient(config);
32
+ const server = new McpServer({
33
+ name: "tickr",
34
+ version: "0.1.0",
35
+ });
36
+ // Registrace tools
37
+ registerCreateTicket(server, api);
38
+ registerListTickets(server, api);
39
+ registerGetTicket(server, api);
40
+ registerUpdateTicket(server, api);
41
+ registerAddComment(server, api);
42
+ registerUpdateImplementationItem(server, api);
43
+ registerSearchTickets(server, api);
44
+ registerTriageList(server, api);
45
+ registerTriageAccept(server, api);
46
+ registerTriageReject(server, api);
47
+ registerListLabels(server, api);
48
+ registerAddLabel(server, api);
49
+ registerRemoveLabel(server, api);
50
+ registerListCycles(server, api);
51
+ registerAssignCycle(server, api);
52
+ registerListEpics(server, api);
53
+ registerAssignEpic(server, api);
54
+ registerPollDevQueue(server, api);
55
+ registerCompleteDevTask(server, api);
56
+ // Registrace resources
57
+ registerTicketResource(server, api);
58
+ registerProjectResource(server, api);
59
+ // Spuštění na stdio transportu
60
+ const transport = new StdioServerTransport();
61
+ await server.connect(transport);
62
+ }
63
+ main().catch((err) => {
64
+ console.error("MCP server failed to start:", err);
65
+ process.exit(1);
66
+ });
67
+ //# sourceMappingURL=index.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 registerProjectResource(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=project-resource.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { formatProjectMarkdown } from "../formatters.js";
2
+ export function registerProjectResource(server, api) {
3
+ server.resource("project", "tickr://project/{slug}", { description: "Get project overview with recent tickets" }, async (uri) => {
4
+ const slug = uri.pathname.split("/").pop();
5
+ const project = await api.get(`/api/projects/${slug}`);
6
+ const tickets = await api.get(`/api/projects/${slug}/tickets?limit=20`);
7
+ return {
8
+ contents: [
9
+ {
10
+ uri: uri.href,
11
+ mimeType: "text/markdown",
12
+ text: formatProjectMarkdown(project, tickets),
13
+ },
14
+ ],
15
+ };
16
+ });
17
+ }
18
+ //# sourceMappingURL=project-resource.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 registerTicketResource(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=ticket-resource.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { formatTicketMarkdown } from "../formatters.js";
2
+ export function registerTicketResource(server, api) {
3
+ server.resource("ticket", "tickr://ticket/{number}", { description: "Get full ticket detail as markdown" }, async (uri) => {
4
+ const number = uri.pathname.split("/").pop();
5
+ const ticket = await api.get(`/api/tickets/${number}`);
6
+ return {
7
+ contents: [
8
+ {
9
+ uri: uri.href,
10
+ mimeType: "text/markdown",
11
+ text: formatTicketMarkdown(ticket),
12
+ },
13
+ ],
14
+ };
15
+ });
16
+ }
17
+ //# sourceMappingURL=ticket-resource.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 registerAddComment(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=add-comment.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ export function registerAddComment(server, api) {
3
+ server.tool("add_comment", "Add a comment to a ticket", {
4
+ number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
5
+ text: z.string().describe("Comment text (markdown)"),
6
+ }, async (params) => {
7
+ try {
8
+ await api.post(`/api/tickets/${params.number}/comments`, {
9
+ text: params.text,
10
+ });
11
+ return {
12
+ content: [
13
+ { type: "text", text: `Comment added to ${params.number}` },
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=add-comment.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 registerAddLabel(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=add-label.d.ts.map
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ export function registerAddLabel(server, api) {
3
+ server.tool("add_label", "Add a label to a ticket", {
4
+ number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
5
+ label_id: z.string().describe("UUID of the label to add"),
6
+ }, async (params) => {
7
+ try {
8
+ await api.post(`/api/tickets/${params.number}/labels/${params.label_id}`);
9
+ return {
10
+ content: [{ type: "text", text: `Label added to ${params.number}` }],
11
+ };
12
+ }
13
+ catch (err) {
14
+ return {
15
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
16
+ isError: true,
17
+ };
18
+ }
19
+ });
20
+ }
21
+ //# sourceMappingURL=add-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 registerAssignCycle(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=assign-cycle.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ export function registerAssignCycle(server, api) {
3
+ server.tool("assign_cycle", "Assign a ticket to a cycle (sprint)", {
4
+ project: z.string().describe("Project slug"),
5
+ cycle_id: z.string().describe("UUID of the cycle"),
6
+ number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
7
+ }, async (params) => {
8
+ try {
9
+ await api.post(`/api/projects/${params.project}/cycles/${params.cycle_id}/tickets/${params.number}`);
10
+ return {
11
+ content: [{ type: "text", text: `${params.number} assigned to cycle ${params.cycle_id}` }],
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=assign-cycle.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 registerAssignEpic(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=assign-epic.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ export function registerAssignEpic(server, api) {
3
+ server.tool("assign_epic", "Assign a ticket to an epic", {
4
+ project: z.string().describe("Project slug"),
5
+ epic_id: z.string().describe("UUID of the epic"),
6
+ number: z.string().describe("Ticket display number, e.g. 'TKR-42' (legacy) or 'TKR-BUG-0042' (typed)"),
7
+ }, async (params) => {
8
+ try {
9
+ await api.post(`/api/projects/${params.project}/epics/${params.epic_id}/tickets/${params.number}`);
10
+ return {
11
+ content: [{ type: "text", text: `${params.number} assigned to epic ${params.epic_id}` }],
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=assign-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 registerCompleteDevTask(server: McpServer, api: ApiClient): void;
4
+ //# sourceMappingURL=complete-dev-task.d.ts.map