@k-system/tickr-mcp 1.28.1 → 1.29.1

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/channel.d.ts CHANGED
@@ -18,6 +18,7 @@
18
18
  * TICKR_TOKEN_ID — Guid tokenId (fallback)
19
19
  * TICKR_AGENT_NAME — Název agenta pro session lookup (default: z config)
20
20
  * TICKR_TENANT_ID — Tenant ID pro device grant
21
+ * TICKR_PROJECT — Project slug filtr (volitelný, pokud chybí = bez filtru)
21
22
  * TICKR_NO_SIGNALR — "1" pro HTTP-only mode (bez SignalR)
22
23
  */
23
24
  export {};
package/dist/channel.js CHANGED
@@ -18,6 +18,7 @@
18
18
  * TICKR_TOKEN_ID — Guid tokenId (fallback)
19
19
  * TICKR_AGENT_NAME — Název agenta pro session lookup (default: z config)
20
20
  * TICKR_TENANT_ID — Tenant ID pro device grant
21
+ * TICKR_PROJECT — Project slug filtr (volitelný, pokud chybí = bez filtru)
21
22
  * TICKR_NO_SIGNALR — "1" pro HTTP-only mode (bez SignalR)
22
23
  */
23
24
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -25,9 +26,42 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
25
26
  import { HubConnectionBuilder } from "@microsoft/signalr";
26
27
  import { loadConfig } from "./config.js";
27
28
  import { getDeviceGrantToken, loadSession, extractJwtClaim, } from "./device-grant.js";
29
+ // --- Helpers ---
30
+ /** Joinne SignalR project groups aby channel dostával PipelineHandoff/DevTaskQueued eventy */
31
+ async function joinProjectGroups(connection, projectFilter, apiUrl, accessTokenFactory) {
32
+ if (projectFilter) {
33
+ try {
34
+ await connection.invoke("JoinProject", projectFilter);
35
+ console.error(`[tickr-channel] Joined project group: ${projectFilter}`);
36
+ }
37
+ catch (err) {
38
+ console.error(`[tickr-channel] Failed to join project group: ${err instanceof Error ? err.message : err}`);
39
+ }
40
+ }
41
+ else {
42
+ // Bez PROJECT_FILTER — získat projekty přes API a joinovat všechny
43
+ try {
44
+ const token = await accessTokenFactory();
45
+ const resp = await fetch(`${apiUrl}/api/projects`, {
46
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
47
+ });
48
+ if (resp.ok) {
49
+ const body = (await resp.json());
50
+ for (const p of body.data ?? []) {
51
+ await connection.invoke("JoinProject", p.slug).catch(() => { });
52
+ }
53
+ console.error(`[tickr-channel] Joined ${body.data?.length ?? 0} project groups`);
54
+ }
55
+ }
56
+ catch (err) {
57
+ console.error(`[tickr-channel] Failed to fetch projects for JoinProject: ${err instanceof Error ? err.message : err}`);
58
+ }
59
+ }
60
+ }
28
61
  // --- Config ---
29
62
  const config = loadConfig();
30
63
  const NO_SIGNALR = process.env.TICKR_NO_SIGNALR === "1";
64
+ const PROJECT_FILTER = process.env.TICKR_PROJECT || null;
31
65
  // --- MCP Channel Server ---
32
66
  const mcp = new Server({ name: "tickr-devqueue", version: "1.0.0" }, {
33
67
  capabilities: {
@@ -151,25 +185,87 @@ if (!NO_SIGNALR) {
151
185
  });
152
186
  // Nový ticket v queue
153
187
  connection.on("DevTaskQueued", async (data) => {
188
+ // Filtruj podle projektu (pokud je TICKR_PROJECT nastaven)
189
+ if (PROJECT_FILTER && data.projectSlug && data.projectSlug !== PROJECT_FILTER)
190
+ return;
154
191
  await sendChannelNotification(`Novy ticket ve fronte: ${data.ticketDisplayNumber ?? "unknown"}`, { event: "task_queued" });
155
192
  });
156
193
  // Pipeline handoff — cílená notifikace
194
+ // Auto-poll: channel server sám zavolá poll_dev_queue a pošle agentovi strukturovaná data
157
195
  connection.on("PipelineHandoff", async (data) => {
158
196
  if (!data.assignedTokenId || data.assignedTokenId !== tokenId)
159
197
  return;
160
- await sendChannelNotification(`Pipeline handoff: ${data.ticketDisplayNumber} "${data.ticketTitle}" [${data.priority}] status: ${data.fromStatus} → ${data.toStatus}. Zavolej poll_dev_queue pro prijeti ukolu.`, {
161
- event: "pipeline_handoff",
162
- ticket: data.ticketDisplayNumber,
163
- priority: data.priority,
164
- from_status: data.fromStatus,
165
- to_status: data.toStatus,
166
- });
198
+ // Filtruj podle projektu (pokud je TICKR_PROJECT nastaven)
199
+ if (PROJECT_FILTER && data.projectSlug && data.projectSlug !== PROJECT_FILTER)
200
+ return;
201
+ let pollResult = null;
202
+ try {
203
+ // Timeout 10s — zabrání zaseknutí handleru pokud API neodpovídá
204
+ const abortController = new AbortController();
205
+ const timeout = setTimeout(() => abortController.abort(), 10_000);
206
+ const token = await accessTokenFactory();
207
+ const pollResponse = await fetch(`${config.apiUrl}/api/dev-queue/poll`, {
208
+ method: "GET",
209
+ headers: {
210
+ Authorization: `Bearer ${token}`,
211
+ Accept: "application/json",
212
+ },
213
+ signal: abortController.signal,
214
+ });
215
+ clearTimeout(timeout);
216
+ if (pollResponse.ok) {
217
+ const body = (await pollResponse.json());
218
+ pollResult = body?.data ?? null;
219
+ console.error(`[tickr-channel] Auto-poll success for ${data.ticketDisplayNumber}: assignmentId=${pollResult?.assignmentId ?? "none"}`);
220
+ }
221
+ else if (pollResponse.status === 204) {
222
+ console.error("[tickr-channel] Auto-poll: no tasks in queue (204)");
223
+ }
224
+ else {
225
+ console.error(`[tickr-channel] Auto-poll failed: HTTP ${pollResponse.status}`);
226
+ }
227
+ }
228
+ catch (err) {
229
+ console.error("[tickr-channel] Auto-poll error:", err);
230
+ }
231
+ const ticket = pollResult?.ticket;
232
+ if (pollResult?.assignmentId && ticket) {
233
+ // Strukturovaná notifikace s daty ticketu — agent může rovnou začít pracovat
234
+ await sendChannelNotification(`Pipeline handoff: ${ticket.displayNumber ?? data.ticketDisplayNumber} "${ticket.title ?? data.ticketTitle}" [${ticket.priority ?? data.priority}] — ukol prirazen (assignmentId: ${pollResult.assignmentId}). Zavolej /check-queue pro zahajeni prace.`, {
235
+ event: "pipeline_handoff",
236
+ ticket: ticket.displayNumber ?? data.ticketDisplayNumber,
237
+ title: ticket.title ?? data.ticketTitle,
238
+ priority: ticket.priority ?? data.priority,
239
+ assignment_id: pollResult.assignmentId,
240
+ from_status: data.fromStatus,
241
+ to_status: data.toStatus,
242
+ auto_polled: true,
243
+ });
244
+ }
245
+ else {
246
+ // Fallback: poll selhal nebo prázdná fronta — pošli původní textovou notifikaci
247
+ await sendChannelNotification(`Pipeline handoff: ${data.ticketDisplayNumber} "${data.ticketTitle}" [${data.priority}] — status: ${data.fromStatus} → ${data.toStatus}. Zavolej poll_dev_queue pro prijeti ukolu.`, {
248
+ event: "pipeline_handoff",
249
+ ticket: data.ticketDisplayNumber,
250
+ priority: data.priority,
251
+ from_status: data.fromStatus,
252
+ to_status: data.toStatus,
253
+ auto_polled: false,
254
+ });
255
+ }
167
256
  });
168
257
  connection.onreconnecting(() => {
169
258
  console.error("[tickr-channel] SignalR reconnecting...");
170
259
  });
171
- connection.onreconnected(() => {
260
+ connection.onreconnected(async () => {
172
261
  console.error("[tickr-channel] SignalR reconnected");
262
+ // Re-join project groups po reconnectu
263
+ try {
264
+ await joinProjectGroups(connection, PROJECT_FILTER, config.apiUrl, accessTokenFactory);
265
+ }
266
+ catch {
267
+ // joinProjectGroups má vlastní error handling, toto je safety net
268
+ }
173
269
  });
174
270
  connection.onclose((err) => {
175
271
  console.error("[tickr-channel] SignalR connection closed", err?.message);
@@ -177,6 +273,8 @@ if (!NO_SIGNALR) {
177
273
  try {
178
274
  await connection.start();
179
275
  console.error(`[tickr-channel] Connected to ${config.apiUrl}/hubs/board, filtering for tokenId=${tokenId}`);
276
+ // Joinni project groups aby channel dostával PipelineHandoff a DevTaskQueued eventy
277
+ await joinProjectGroups(connection, PROJECT_FILTER, config.apiUrl, accessTokenFactory);
180
278
  }
181
279
  catch (err) {
182
280
  console.error(`[tickr-channel] SignalR connection failed: ${err instanceof Error ? err.message : err}`);
package/dist/server.js CHANGED
@@ -112,7 +112,7 @@ export async function startServer() {
112
112
  }
113
113
  const server = new McpServer({
114
114
  name: "tickr",
115
- version: "1.28.1",
115
+ version: "1.29.1",
116
116
  });
117
117
  // Debug logging wrapper (dedup odstraněn — nefunkční cross-process, řeší se na API straně: TKR-ADR-0043)
118
118
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@k-system/tickr-mcp",
3
- "version": "1.28.1",
4
- "description": "MCP server for Tickr project management — 56 tools + setup CLI wizard",
3
+ "version": "1.29.1",
4
+ "description": "MCP server for Tickr project management — 74 tools + setup CLI wizard",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {