@sixtynine-digital/blitz-mcp 0.2.1 → 0.4.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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as loadConfig, t as createServer$1 } from "./server-pwmkXBAV.mjs";
2
+ import { n as loadConfig, t as createServer$1 } from "./server-BIEP9XVB.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { createServer } from "node:http";
5
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -78,7 +78,8 @@ function startHttpServer() {
78
78
  const config = loadConfig({
79
79
  apiUrl,
80
80
  apiKey,
81
- workspaceSlug
81
+ workspaceSlug,
82
+ projectId: query.project_id || process.env.BLITZ_PROJECT_ID || void 0
82
83
  });
83
84
  const transport = new StreamableHTTPServerTransport({
84
85
  sessionIdGenerator: () => randomUUID(),
@@ -137,4 +138,4 @@ function startHttpServer() {
137
138
 
138
139
  //#endregion
139
140
  export { startHttpServer };
140
- //# sourceMappingURL=http-Dk8tMK0J.mjs.map
141
+ //# sourceMappingURL=http-DqMYQFKB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-DqMYQFKB.mjs","names":["params: Record<string, string>","createHttpServer","body","createServer"],"sources":["../src/http.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createServer as createHttpServer } from \"node:http\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport { loadConfig } from \"./config.js\";\nimport { createServer } from \"./server.js\";\n\ninterface SessionEntry {\n transport: StreamableHTTPServerTransport;\n server: ReturnType<typeof createServer>;\n}\n\nconst sessions = new Map<string, SessionEntry>();\n\nfunction parseBody(req: import(\"node:http\").IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = \"\";\n req.on(\"data\", (chunk: Buffer) => (data += chunk.toString()));\n req.on(\"end\", () => {\n try {\n resolve(JSON.parse(data));\n } catch {\n reject(new Error(\"Invalid JSON\"));\n }\n });\n req.on(\"error\", reject);\n });\n}\n\nfunction jsonResponse(\n res: import(\"node:http\").ServerResponse,\n status: number,\n body: unknown\n) {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n\nfunction parseQueryParams(url: string): Record<string, string> {\n const idx = url.indexOf(\"?\");\n if (idx === -1) return {};\n const params: Record<string, string> = {};\n const search = url.slice(idx + 1);\n for (const pair of search.split(\"&\")) {\n const [key, val] = pair.split(\"=\");\n if (key) params[decodeURIComponent(key)] = decodeURIComponent(val || \"\");\n }\n return params;\n}\n\nexport function startHttpServer() {\n const port = parseInt(process.env.BLITZ_MCP_PORT || \"3100\", 10);\n const apiUrl = process.env.BLITZ_API_URL;\n\n if (!apiUrl) {\n throw new Error(\"BLITZ_API_URL environment variable is required\");\n }\n\n const httpServer = createHttpServer(async (req, res) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n \"Content-Type, mcp-session-id\"\n );\n res.setHeader(\"Access-Control-Expose-Headers\", \"mcp-session-id\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const urlPath = (req.url || \"\").split(\"?\")[0];\n\n if (urlPath !== \"/mcp\") {\n jsonResponse(res, 404, { error: \"Not found\" });\n return;\n }\n\n if (req.method === \"POST\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n const body = await parseBody(req);\n await entry.transport.handleRequest(req, res, body);\n return;\n }\n\n const body = (await parseBody(req)) as Record<string, unknown>;\n\n if (!sessionId && isInitializeRequest(body)) {\n // Read per-user credentials from query params\n const query = parseQueryParams(req.url || \"\");\n const apiKey = query.api_key || process.env.BLITZ_API_KEY;\n const workspaceSlug = query.workspace || process.env.BLITZ_WORKSPACE_SLUG;\n\n if (!apiKey) {\n jsonResponse(res, 401, {\n error: \"API key required. Add ?api_key=plane_api_XXX to the URL\",\n });\n return;\n }\n\n if (!workspaceSlug) {\n jsonResponse(res, 400, {\n error:\n \"Workspace slug required. Add ?workspace=YOUR_SLUG to the URL\",\n });\n return;\n }\n\n const projectId = query.project_id || process.env.BLITZ_PROJECT_ID;\n\n const config = loadConfig({\n apiUrl,\n apiKey,\n workspaceSlug,\n projectId: projectId || undefined,\n });\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (id) => {\n sessions.set(id, { transport, server });\n },\n });\n\n transport.onclose = () => {\n if (transport.sessionId) {\n sessions.delete(transport.sessionId);\n }\n };\n\n const server = createServer(config);\n await server.connect(transport);\n await transport.handleRequest(req, res, body);\n return;\n }\n\n jsonResponse(res, 400, {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Invalid session\" },\n id: null,\n });\n return;\n }\n\n if (req.method === \"GET\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string;\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n await entry.transport.handleRequest(req, res);\n } else {\n jsonResponse(res, 400, { error: \"Invalid session\" });\n }\n return;\n }\n\n if (req.method === \"DELETE\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string;\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n await entry.transport.handleRequest(req, res);\n } else {\n jsonResponse(res, 400, { error: \"Invalid session\" });\n }\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n });\n\n httpServer.listen(port, \"0.0.0.0\", () => {\n console.log(`Blitz MCP HTTP server running on port ${port}`);\n console.log(`Endpoint: http://0.0.0.0:${port}/mcp`);\n });\n\n process.on(\"SIGINT\", () => {\n httpServer.close();\n process.exit(0);\n });\n\n process.on(\"SIGTERM\", () => {\n httpServer.close();\n process.exit(0);\n });\n}\n"],"mappings":";;;;;;;;AAYA,MAAM,2BAAW,IAAI,KAA2B;AAEhD,SAAS,UAAU,KAA4D;AAC7E,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,OAAO;AACX,MAAI,GAAG,SAAS,UAAmB,QAAQ,MAAM,UAAU,CAAE;AAC7D,MAAI,GAAG,aAAa;AAClB,OAAI;AACF,YAAQ,KAAK,MAAM,KAAK,CAAC;WACnB;AACN,2BAAO,IAAI,MAAM,eAAe,CAAC;;IAEnC;AACF,MAAI,GAAG,SAAS,OAAO;GACvB;;AAGJ,SAAS,aACP,KACA,QACA,MACA;AACA,KAAI,UAAU,QAAQ,EAAE,gBAAgB,oBAAoB,CAAC;AAC7D,KAAI,IAAI,KAAK,UAAU,KAAK,CAAC;;AAG/B,SAAS,iBAAiB,KAAqC;CAC7D,MAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,KAAI,QAAQ,GAAI,QAAO,EAAE;CACzB,MAAMA,SAAiC,EAAE;CACzC,MAAM,SAAS,IAAI,MAAM,MAAM,EAAE;AACjC,MAAK,MAAM,QAAQ,OAAO,MAAM,IAAI,EAAE;EACpC,MAAM,CAAC,KAAK,OAAO,KAAK,MAAM,IAAI;AAClC,MAAI,IAAK,QAAO,mBAAmB,IAAI,IAAI,mBAAmB,OAAO,GAAG;;AAE1E,QAAO;;AAGT,SAAgB,kBAAkB;CAChC,MAAM,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ,GAAG;CAC/D,MAAM,SAAS,QAAQ,IAAI;AAE3B,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,iDAAiD;CAGnE,MAAM,aAAaC,aAAiB,OAAO,KAAK,QAAQ;AACtD,MAAI,UAAU,+BAA+B,IAAI;AACjD,MAAI,UAAU,gCAAgC,6BAA6B;AAC3E,MAAI,UACF,gCACA,+BACD;AACD,MAAI,UAAU,iCAAiC,iBAAiB;AAEhE,MAAI,IAAI,WAAW,WAAW;AAC5B,OAAI,UAAU,IAAI;AAClB,OAAI,KAAK;AACT;;AAKF,OAFiB,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC,OAE3B,QAAQ;AACtB,gBAAa,KAAK,KAAK,EAAE,OAAO,aAAa,CAAC;AAC9C;;AAGF,MAAI,IAAI,WAAW,QAAQ;GACzB,MAAM,YAAY,IAAI,QAAQ;AAE9B,OAAI,aAAa,SAAS,IAAI,UAAU,EAAE;IACxC,MAAM,QAAQ,SAAS,IAAI,UAAU;IACrC,MAAMC,SAAO,MAAM,UAAU,IAAI;AACjC,UAAM,MAAM,UAAU,cAAc,KAAK,KAAKA,OAAK;AACnD;;GAGF,MAAM,OAAQ,MAAM,UAAU,IAAI;AAElC,OAAI,CAAC,aAAa,oBAAoB,KAAK,EAAE;IAE3C,MAAM,QAAQ,iBAAiB,IAAI,OAAO,GAAG;IAC7C,MAAM,SAAS,MAAM,WAAW,QAAQ,IAAI;IAC5C,MAAM,gBAAgB,MAAM,aAAa,QAAQ,IAAI;AAErD,QAAI,CAAC,QAAQ;AACX,kBAAa,KAAK,KAAK,EACrB,OAAO,2DACR,CAAC;AACF;;AAGF,QAAI,CAAC,eAAe;AAClB,kBAAa,KAAK,KAAK,EACrB,OACE,gEACH,CAAC;AACF;;IAKF,MAAM,SAAS,WAAW;KACxB;KACA;KACA;KACA,WANgB,MAAM,cAAc,QAAQ,IAAI,oBAMxB;KACzB,CAAC;IAEF,MAAM,YAAY,IAAI,8BAA8B;KAClD,0BAA0B,YAAY;KACtC,uBAAuB,OAAO;AAC5B,eAAS,IAAI,IAAI;OAAE;OAAW;OAAQ,CAAC;;KAE1C,CAAC;AAEF,cAAU,gBAAgB;AACxB,SAAI,UAAU,UACZ,UAAS,OAAO,UAAU,UAAU;;IAIxC,MAAM,SAASC,eAAa,OAAO;AACnC,UAAM,OAAO,QAAQ,UAAU;AAC/B,UAAM,UAAU,cAAc,KAAK,KAAK,KAAK;AAC7C;;AAGF,gBAAa,KAAK,KAAK;IACrB,SAAS;IACT,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAmB;IACnD,IAAI;IACL,CAAC;AACF;;AAGF,MAAI,IAAI,WAAW,OAAO;GACxB,MAAM,YAAY,IAAI,QAAQ;AAC9B,OAAI,aAAa,SAAS,IAAI,UAAU,CAEtC,OADc,SAAS,IAAI,UAAU,CACzB,UAAU,cAAc,KAAK,IAAI;OAE7C,cAAa,KAAK,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAEtD;;AAGF,MAAI,IAAI,WAAW,UAAU;GAC3B,MAAM,YAAY,IAAI,QAAQ;AAC9B,OAAI,aAAa,SAAS,IAAI,UAAU,CAEtC,OADc,SAAS,IAAI,UAAU,CACzB,UAAU,cAAc,KAAK,IAAI;OAE7C,cAAa,KAAK,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAEtD;;AAGF,eAAa,KAAK,KAAK,EAAE,OAAO,sBAAsB,CAAC;GACvD;AAEF,YAAW,OAAO,MAAM,iBAAiB;AACvC,UAAQ,IAAI,yCAAyC,OAAO;AAC5D,UAAQ,IAAI,4BAA4B,KAAK,MAAM;GACnD;AAEF,SAAQ,GAAG,gBAAgB;AACzB,aAAW,OAAO;AAClB,UAAQ,KAAK,EAAE;GACf;AAEF,SAAQ,GAAG,iBAAiB;AAC1B,aAAW,OAAO;AAClB,UAAQ,KAAK,EAAE;GACf"}
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { n as loadConfig, t as createServer } from "./server-pwmkXBAV.mjs";
2
+ import { n as loadConfig, t as createServer } from "./server-BIEP9XVB.mjs";
3
3
 
4
4
  //#region src/index.ts
5
5
  if ((process.env.BLITZ_MCP_MODE || "stdio") === "http") {
6
- const { startHttpServer } = await import("./http-Dk8tMK0J.mjs");
6
+ const { startHttpServer } = await import("./http-DqMYQFKB.mjs");
7
7
  startHttpServer();
8
8
  } else {
9
9
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
@@ -0,0 +1,552 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { z } from "zod";
4
+
5
+ //#region src/config.ts
6
+ function loadConfig(overrides) {
7
+ const apiUrl = overrides?.apiUrl || process.env.BLITZ_API_URL;
8
+ const apiKey = overrides?.apiKey || process.env.BLITZ_API_KEY;
9
+ const workspaceSlug = overrides?.workspaceSlug || process.env.BLITZ_WORKSPACE_SLUG;
10
+ const projectId = overrides?.projectId || process.env.BLITZ_PROJECT_ID;
11
+ const missing = [];
12
+ if (!apiUrl) missing.push("BLITZ_API_URL");
13
+ if (!apiKey) missing.push("BLITZ_API_KEY");
14
+ if (!workspaceSlug) missing.push("BLITZ_WORKSPACE_SLUG");
15
+ if (missing.length > 0) throw new Error(`Missing required environment variables: ${missing.join(", ")}\n\nPlease set the following:
16
+ BLITZ_API_URL - Blitz instance URL (e.g. http://localhost:8000)
17
+ BLITZ_API_KEY - API key (format: plane_api_<hex>)
18
+ BLITZ_WORKSPACE_SLUG - Workspace slug
19
+ BLITZ_PROJECT_ID - (Optional) Default project UUID`);
20
+ return {
21
+ apiUrl: apiUrl.replace(/\/+$/, ""),
22
+ apiKey,
23
+ workspaceSlug,
24
+ projectId: projectId || void 0
25
+ };
26
+ }
27
+ function resolveProjectId(projectId, config) {
28
+ const id = projectId || config.projectId;
29
+ if (!id) throw new Error("project_id is required. Either pass it as a parameter or set BLITZ_PROJECT_ID environment variable.");
30
+ return id;
31
+ }
32
+
33
+ //#endregion
34
+ //#region src/client.ts
35
+ var BlitzClient = class {
36
+ baseUrl;
37
+ apiKey;
38
+ constructor(config) {
39
+ this.baseUrl = `${config.apiUrl}/api/v1`;
40
+ this.apiKey = config.apiKey;
41
+ }
42
+ async request(method, path, body) {
43
+ const url = `${this.baseUrl}/${path.replace(/^\/+/, "")}`;
44
+ const headers = {
45
+ "X-Api-Key": this.apiKey,
46
+ "Content-Type": "application/json"
47
+ };
48
+ const response = await fetch(url, {
49
+ method,
50
+ headers,
51
+ body: body ? JSON.stringify(body) : void 0
52
+ });
53
+ if (response.status === 429) {
54
+ const retryAfter = response.headers.get("Retry-After");
55
+ throw new Error(`Rate limit exceeded. ${retryAfter ? `Retry after ${retryAfter} seconds.` : "Please wait before retrying."}`);
56
+ }
57
+ if (response.status === 204) return;
58
+ const data = await response.json();
59
+ if (!response.ok) {
60
+ const message = typeof data === "object" && data !== null ? JSON.stringify(data, null, 2) : String(data);
61
+ throw new Error(`API error (${response.status}): ${message}`);
62
+ }
63
+ return data;
64
+ }
65
+ async get(path) {
66
+ return this.request("GET", path);
67
+ }
68
+ async post(path, body) {
69
+ return this.request("POST", path, body);
70
+ }
71
+ async patch(path, body) {
72
+ return this.request("PATCH", path, body);
73
+ }
74
+ async delete(path) {
75
+ return this.request("DELETE", path);
76
+ }
77
+ };
78
+
79
+ //#endregion
80
+ //#region src/tools/projects.ts
81
+ function registerProjectTools(server, client, config) {
82
+ server.tool("list-projects", "List all projects in the Blitz workspace. Returns project names, identifiers, and IDs.", {}, async () => {
83
+ const data = await client.get(`workspaces/${config.workspaceSlug}/projects/`);
84
+ return { content: [{
85
+ type: "text",
86
+ text: JSON.stringify(data, null, 2)
87
+ }] };
88
+ });
89
+ }
90
+
91
+ //#endregion
92
+ //#region src/tools/states.ts
93
+ function registerStateTools(server, client, config) {
94
+ server.tool("list-states", "List all workflow states for a project. Use this to find valid state IDs when creating or updating work items.", { project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)") }, async ({ project_id }) => {
95
+ const pid = resolveProjectId(project_id, config);
96
+ const data = await client.get(`workspaces/${config.workspaceSlug}/projects/${pid}/states/`);
97
+ return { content: [{
98
+ type: "text",
99
+ text: JSON.stringify(data, null, 2)
100
+ }] };
101
+ });
102
+ }
103
+
104
+ //#endregion
105
+ //#region src/tools/labels.ts
106
+ function registerLabelTools(server, client, config) {
107
+ server.tool("list-labels", "List all labels for a project. Use this to find valid label IDs when creating or updating work items.", { project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)") }, async ({ project_id }) => {
108
+ const pid = resolveProjectId(project_id, config);
109
+ const data = await client.get(`workspaces/${config.workspaceSlug}/projects/${pid}/labels/`);
110
+ return { content: [{
111
+ type: "text",
112
+ text: JSON.stringify(data, null, 2)
113
+ }] };
114
+ });
115
+ server.tool("create-label", "Create a new label in a project.", {
116
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
117
+ name: z.string().min(1).max(255).describe("Label name"),
118
+ color: z.string().optional().describe("Hex color (e.g. #ff0000)"),
119
+ description: z.string().optional().describe("Label description")
120
+ }, async ({ project_id,...body }) => {
121
+ const pid = resolveProjectId(project_id, config);
122
+ const data = await client.post(`workspaces/${config.workspaceSlug}/projects/${pid}/labels/`, body);
123
+ return { content: [{
124
+ type: "text",
125
+ text: JSON.stringify(data, null, 2)
126
+ }] };
127
+ });
128
+ }
129
+
130
+ //#endregion
131
+ //#region src/tools/search.ts
132
+ function registerSearchTools(server, client, config) {
133
+ server.tool("search-work-items", "Search for work items (tasks/issues) across the workspace by text query. Searches in title and identifier.", {
134
+ query: z.string().min(1).describe("Search text"),
135
+ project_id: z.string().uuid().optional().describe("Optional project UUID to scope the search (defaults to BLITZ_PROJECT_ID if set)")
136
+ }, async ({ query, project_id }) => {
137
+ const pid = project_id || config.projectId;
138
+ let path = `workspaces/${config.workspaceSlug}/work-items/search/?search=${encodeURIComponent(query)}`;
139
+ if (pid) path += `&project_id=${pid}`;
140
+ const data = await client.get(path);
141
+ return { content: [{
142
+ type: "text",
143
+ text: JSON.stringify(data, null, 2)
144
+ }] };
145
+ });
146
+ }
147
+
148
+ //#endregion
149
+ //#region src/tools/members.ts
150
+ var MemberCache = class {
151
+ cache = /* @__PURE__ */ new Map();
152
+ ttlMs;
153
+ constructor(ttlMs = 300 * 1e3) {
154
+ this.ttlMs = ttlMs;
155
+ }
156
+ get(key) {
157
+ const entry = this.cache.get(key);
158
+ if (!entry || Date.now() - entry.fetchedAt > this.ttlMs) {
159
+ if (entry) this.cache.delete(key);
160
+ return null;
161
+ }
162
+ return entry.members;
163
+ }
164
+ set(key, members) {
165
+ this.cache.set(key, {
166
+ members,
167
+ fetchedAt: Date.now()
168
+ });
169
+ }
170
+ };
171
+ const cache = new MemberCache();
172
+ async function fetchProjectMembers(client, workspaceSlug, projectId) {
173
+ const key = `project:${projectId}`;
174
+ const cached = cache.get(key);
175
+ if (cached) return cached;
176
+ const data = await client.get(`workspaces/${workspaceSlug}/projects/${projectId}/members/`);
177
+ cache.set(key, data);
178
+ return data;
179
+ }
180
+ async function fetchWorkspaceMembers(client, workspaceSlug) {
181
+ const key = `workspace:${workspaceSlug}`;
182
+ const cached = cache.get(key);
183
+ if (cached) return cached;
184
+ const data = await client.get(`workspaces/${workspaceSlug}/members/`);
185
+ cache.set(key, data);
186
+ return data;
187
+ }
188
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
189
+ async function resolveAssignees(values, client, workspaceSlug, projectId) {
190
+ if (values.every((v) => UUID_RE.test(v))) return values;
191
+ const members = await fetchProjectMembers(client, workspaceSlug, projectId);
192
+ return values.map((value) => {
193
+ if (UUID_RE.test(value)) return value;
194
+ const lower = value.toLowerCase();
195
+ const match = members.find((m) => m.display_name.toLowerCase() === lower || `${m.first_name} ${m.last_name}`.toLowerCase().trim() === lower || m.email.toLowerCase() === lower);
196
+ if (!match) throw new Error(`Could not resolve assignee "${value}". Use list-members to see available members.`);
197
+ return match.id;
198
+ });
199
+ }
200
+ function enrichAssigneeNames(item, members) {
201
+ const assigneeIds = item.assignees;
202
+ if (!Array.isArray(assigneeIds) || assigneeIds.length === 0) return item;
203
+ const memberMap = new Map(members.map((m) => [m.id, m.display_name]));
204
+ const assignees = assigneeIds.map((id) => ({
205
+ id,
206
+ display_name: memberMap.get(id) ?? "Unknown"
207
+ }));
208
+ return {
209
+ ...item,
210
+ assignees
211
+ };
212
+ }
213
+ function registerMemberTools(server, client, config) {
214
+ const { workspaceSlug } = config;
215
+ server.tool("list-members", "List members of a project or workspace. Use this to find user IDs and display names for assigning work items. You can also assign work items by display name directly.", { project_id: z.string().uuid().optional().describe("Project UUID - lists project members. Omit to list workspace members (requires admin).") }, async ({ project_id }) => {
216
+ let members;
217
+ if (project_id || config.projectId) members = await fetchProjectMembers(client, workspaceSlug, project_id || config.projectId);
218
+ else members = await fetchWorkspaceMembers(client, workspaceSlug);
219
+ return { content: [{
220
+ type: "text",
221
+ text: JSON.stringify(members, null, 2)
222
+ }] };
223
+ });
224
+ }
225
+
226
+ //#endregion
227
+ //#region src/tools/work-items.ts
228
+ const STATE_GROUPS = [
229
+ "backlog",
230
+ "unstarted",
231
+ "started",
232
+ "completed",
233
+ "cancelled"
234
+ ];
235
+ const SUMMARY_FIELDS = [
236
+ "id",
237
+ "sequence_id",
238
+ "name",
239
+ "state",
240
+ "priority",
241
+ "assignees",
242
+ "labels",
243
+ "start_date",
244
+ "target_date",
245
+ "created_at",
246
+ "updated_at",
247
+ "completed_at"
248
+ ];
249
+ const HEAVY_FIELDS = [
250
+ "description_html",
251
+ "description_binary",
252
+ "description_stripped",
253
+ "description",
254
+ "issue_reactions",
255
+ "issue_attachments",
256
+ "issue_link"
257
+ ];
258
+ function buildListParams(opts) {
259
+ const params = new URLSearchParams();
260
+ if (opts.cursor) params.set("cursor", opts.cursor);
261
+ if (opts.per_page) params.set("per_page", String(opts.per_page));
262
+ if (opts.order_by) params.set("order_by", opts.order_by);
263
+ if (opts.state_group?.length) params.set("state_group", opts.state_group.join(","));
264
+ if (opts.state_id?.length) params.set("state", opts.state_id.join(","));
265
+ return params;
266
+ }
267
+ function stripHeavyFields(item) {
268
+ const result = { ...item };
269
+ for (const field of HEAVY_FIELDS) delete result[field];
270
+ return result;
271
+ }
272
+ function pickSummaryFields(item) {
273
+ const result = {};
274
+ for (const field of SUMMARY_FIELDS) if (field in item) result[field] = item[field];
275
+ return result;
276
+ }
277
+ function mapBodyForApi(body) {
278
+ const mapped = { ...body };
279
+ if ("state_id" in mapped) {
280
+ mapped.state = mapped.state_id;
281
+ delete mapped.state_id;
282
+ }
283
+ if ("parent_id" in mapped) {
284
+ mapped.parent = mapped.parent_id;
285
+ delete mapped.parent_id;
286
+ }
287
+ return mapped;
288
+ }
289
+ function registerWorkItemTools(server, client, config) {
290
+ const { workspaceSlug } = config;
291
+ server.tool("list-work-items", "List work items in a project with filtering, pagination and sorting. Returns all fields except heavy description content.", {
292
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
293
+ cursor: z.string().optional().describe("Pagination cursor from previous response"),
294
+ per_page: z.number().min(1).max(100).optional().describe("Items per page (default 20)"),
295
+ order_by: z.string().optional().describe("Sort field (e.g. -created_at, priority, state)"),
296
+ state_group: z.array(z.enum(STATE_GROUPS)).optional().describe("Filter by state group: backlog, unstarted, started, completed, cancelled"),
297
+ state_id: z.array(z.string().uuid()).optional().describe("Filter by specific state UUIDs")
298
+ }, async ({ project_id, cursor, per_page, order_by, state_group, state_id }) => {
299
+ const pid = resolveProjectId(project_id, config);
300
+ const query = buildListParams({
301
+ cursor,
302
+ per_page,
303
+ order_by,
304
+ state_group,
305
+ state_id
306
+ }).toString();
307
+ const path = `workspaces/${workspaceSlug}/projects/${pid}/work-items/${query ? `?${query}` : ""}`;
308
+ const data = await client.get(path);
309
+ if (data.results && Array.isArray(data.results)) {
310
+ const members = await fetchProjectMembers(client, workspaceSlug, pid);
311
+ data.results = data.results.map((item) => enrichAssigneeNames(stripHeavyFields(item), members));
312
+ }
313
+ return { content: [{
314
+ type: "text",
315
+ text: JSON.stringify(data, null, 2)
316
+ }] };
317
+ });
318
+ server.tool("list-work-items-summary", "List work items with minimal fields only: name, state, priority, dates. Use this for overviews and large projects.", {
319
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
320
+ cursor: z.string().optional().describe("Pagination cursor from previous response"),
321
+ per_page: z.number().min(1).max(100).optional().describe("Items per page (default 20)"),
322
+ order_by: z.string().optional().describe("Sort field (e.g. -created_at, priority, state)"),
323
+ state_group: z.array(z.enum(STATE_GROUPS)).optional().describe("Filter by state group: backlog, unstarted, started, completed, cancelled"),
324
+ state_id: z.array(z.string().uuid()).optional().describe("Filter by specific state UUIDs")
325
+ }, async ({ project_id, cursor, per_page, order_by, state_group, state_id }) => {
326
+ const pid = resolveProjectId(project_id, config);
327
+ const query = buildListParams({
328
+ cursor,
329
+ per_page,
330
+ order_by,
331
+ state_group,
332
+ state_id
333
+ }).toString();
334
+ const path = `workspaces/${workspaceSlug}/projects/${pid}/work-items/${query ? `?${query}` : ""}`;
335
+ const data = await client.get(path);
336
+ if (data.results && Array.isArray(data.results)) {
337
+ const members = await fetchProjectMembers(client, workspaceSlug, pid);
338
+ data.results = data.results.map((item) => enrichAssigneeNames(pickSummaryFields(item), members));
339
+ }
340
+ return { content: [{
341
+ type: "text",
342
+ text: JSON.stringify(data, null, 2)
343
+ }] };
344
+ });
345
+ server.tool("get-work-item", "Get a work item by its human-readable identifier (e.g. BLZ-42). Returns full details.", { identifier: z.string().min(1).describe("Work item identifier (e.g. PROJ-123)") }, async ({ identifier }) => {
346
+ const data = await client.get(`workspaces/${workspaceSlug}/work-items/${identifier}/`);
347
+ const projectId = data.project_id ?? data.project;
348
+ if (projectId && Array.isArray(data.assignees)) try {
349
+ const members = await fetchProjectMembers(client, workspaceSlug, projectId);
350
+ return { content: [{
351
+ type: "text",
352
+ text: JSON.stringify(enrichAssigneeNames(data, members), null, 2)
353
+ }] };
354
+ } catch {}
355
+ return { content: [{
356
+ type: "text",
357
+ text: JSON.stringify(data, null, 2)
358
+ }] };
359
+ });
360
+ server.tool("get-work-item-by-id", "Get a work item by its project ID and work item UUID.", {
361
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
362
+ work_item_id: z.string().uuid().describe("Work item UUID")
363
+ }, async ({ project_id, work_item_id }) => {
364
+ const pid = resolveProjectId(project_id, config);
365
+ const data = await client.get(`workspaces/${workspaceSlug}/projects/${pid}/work-items/${work_item_id}/`);
366
+ const members = await fetchProjectMembers(client, workspaceSlug, pid);
367
+ return { content: [{
368
+ type: "text",
369
+ text: JSON.stringify(enrichAssigneeNames(data, members), null, 2)
370
+ }] };
371
+ });
372
+ server.tool("create-work-item", "Create a new work item (task/issue) in a project. Use list-projects to find project IDs, list-states for state IDs, and list-labels for label IDs.", {
373
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
374
+ name: z.string().min(1).max(255).describe("Work item title"),
375
+ description_html: z.string().optional().describe("HTML description content"),
376
+ priority: z.enum([
377
+ "urgent",
378
+ "high",
379
+ "medium",
380
+ "low",
381
+ "none"
382
+ ]).optional().describe("Priority level"),
383
+ state_id: z.string().uuid().optional().describe("State UUID (use list-states to find valid IDs)"),
384
+ assignees: z.array(z.string()).optional().describe("Array of user UUIDs or display names to assign. Use list-members to find valid names."),
385
+ labels: z.array(z.string().uuid()).optional().describe("Array of label UUIDs"),
386
+ start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
387
+ target_date: z.string().optional().describe("Target date (YYYY-MM-DD)"),
388
+ parent_id: z.string().uuid().optional().describe("Parent work item UUID for sub-issues")
389
+ }, async ({ project_id,...fields }) => {
390
+ const pid = resolveProjectId(project_id, config);
391
+ const resolvedFields = { ...fields };
392
+ if (fields.assignees?.length) resolvedFields.assignees = await resolveAssignees(fields.assignees, client, workspaceSlug, pid);
393
+ const body = mapBodyForApi(resolvedFields);
394
+ const data = await client.post(`workspaces/${workspaceSlug}/projects/${pid}/work-items/`, body);
395
+ return { content: [{
396
+ type: "text",
397
+ text: JSON.stringify(data, null, 2)
398
+ }] };
399
+ });
400
+ server.tool("update-work-item", "Update an existing work item. Only provide fields you want to change.", {
401
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
402
+ work_item_id: z.string().uuid().describe("Work item UUID"),
403
+ name: z.string().min(1).max(255).optional().describe("New title"),
404
+ description_html: z.string().optional().describe("New HTML description"),
405
+ priority: z.enum([
406
+ "urgent",
407
+ "high",
408
+ "medium",
409
+ "low",
410
+ "none"
411
+ ]).optional().describe("New priority level"),
412
+ state_id: z.string().uuid().optional().describe("New state UUID (use list-states to find valid IDs)"),
413
+ assignees: z.array(z.string()).optional().describe("New assignee UUIDs or display names (replaces existing). Use list-members to find valid names."),
414
+ labels: z.array(z.string().uuid()).optional().describe("New label UUIDs (replaces existing)"),
415
+ start_date: z.string().nullable().optional().describe("Start date (YYYY-MM-DD) or null to clear"),
416
+ target_date: z.string().nullable().optional().describe("Target date (YYYY-MM-DD) or null to clear")
417
+ }, async ({ project_id, work_item_id,...fields }) => {
418
+ const pid = resolveProjectId(project_id, config);
419
+ const resolvedFields = { ...fields };
420
+ if (fields.assignees?.length) resolvedFields.assignees = await resolveAssignees(fields.assignees, client, workspaceSlug, pid);
421
+ const body = mapBodyForApi(resolvedFields);
422
+ const data = await client.patch(`workspaces/${workspaceSlug}/projects/${pid}/work-items/${work_item_id}/`, body);
423
+ return { content: [{
424
+ type: "text",
425
+ text: JSON.stringify(data, null, 2)
426
+ }] };
427
+ });
428
+ server.tool("delete-work-item", "Delete a work item. This action cannot be undone.", {
429
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
430
+ work_item_id: z.string().uuid().describe("Work item UUID")
431
+ }, async ({ project_id, work_item_id }) => {
432
+ const pid = resolveProjectId(project_id, config);
433
+ await client.delete(`workspaces/${workspaceSlug}/projects/${pid}/work-items/${work_item_id}/`);
434
+ return { content: [{
435
+ type: "text",
436
+ text: "Work item deleted successfully."
437
+ }] };
438
+ });
439
+ }
440
+
441
+ //#endregion
442
+ //#region src/tools/comments.ts
443
+ function registerCommentTools(server, client, config) {
444
+ server.tool("list-comments", "List all comments on a work item.", {
445
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
446
+ work_item_id: z.string().uuid().describe("Work item UUID")
447
+ }, async ({ project_id, work_item_id }) => {
448
+ const pid = resolveProjectId(project_id, config);
449
+ const data = await client.get(`workspaces/${config.workspaceSlug}/projects/${pid}/work-items/${work_item_id}/comments/`);
450
+ return { content: [{
451
+ type: "text",
452
+ text: JSON.stringify(data, null, 2)
453
+ }] };
454
+ });
455
+ server.tool("add-comment", "Add a comment to a work item.", {
456
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
457
+ work_item_id: z.string().uuid().describe("Work item UUID"),
458
+ comment_html: z.string().min(1).describe("Comment content in HTML format")
459
+ }, async ({ project_id, work_item_id, comment_html }) => {
460
+ const pid = resolveProjectId(project_id, config);
461
+ const data = await client.post(`workspaces/${config.workspaceSlug}/projects/${pid}/work-items/${work_item_id}/comments/`, { comment_html });
462
+ return { content: [{
463
+ type: "text",
464
+ text: JSON.stringify(data, null, 2)
465
+ }] };
466
+ });
467
+ server.tool("list-activities", "View the activity history (changelog) of a work item. Shows what changed, when, and by whom.", {
468
+ project_id: z.string().uuid().optional().describe("Project UUID (optional if BLITZ_PROJECT_ID is set)"),
469
+ work_item_id: z.string().uuid().describe("Work item UUID")
470
+ }, async ({ project_id, work_item_id }) => {
471
+ const pid = resolveProjectId(project_id, config);
472
+ const data = await client.get(`workspaces/${config.workspaceSlug}/projects/${pid}/work-items/${work_item_id}/activities/`);
473
+ return { content: [{
474
+ type: "text",
475
+ text: JSON.stringify(data, null, 2)
476
+ }] };
477
+ });
478
+ }
479
+
480
+ //#endregion
481
+ //#region src/tools/index.ts
482
+ function registerAllTools(server, client, config) {
483
+ registerProjectTools(server, client, config);
484
+ registerStateTools(server, client, config);
485
+ registerLabelTools(server, client, config);
486
+ registerSearchTools(server, client, config);
487
+ registerWorkItemTools(server, client, config);
488
+ registerCommentTools(server, client, config);
489
+ registerMemberTools(server, client, config);
490
+ }
491
+
492
+ //#endregion
493
+ //#region src/resources/projects.ts
494
+ function registerProjectResources(server, client, config) {
495
+ const { workspaceSlug } = config;
496
+ server.registerResource("projects", "blitz://projects", {
497
+ description: "List of all projects in the Blitz workspace with their identifiers, names, and IDs.",
498
+ mimeType: "application/json"
499
+ }, async (uri) => {
500
+ const data = await client.get(`workspaces/${workspaceSlug}/projects/`);
501
+ return { contents: [{
502
+ uri: uri.href,
503
+ mimeType: "application/json",
504
+ text: JSON.stringify(data, null, 2)
505
+ }] };
506
+ });
507
+ server.registerResource("project-states", new ResourceTemplate("blitz://projects/{project_id}/states", { list: void 0 }), {
508
+ description: "Workflow states for a specific project.",
509
+ mimeType: "application/json"
510
+ }, async (uri, { project_id }) => {
511
+ const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/states/`);
512
+ return { contents: [{
513
+ uri: uri.href,
514
+ mimeType: "application/json",
515
+ text: JSON.stringify(data, null, 2)
516
+ }] };
517
+ });
518
+ server.registerResource("project-labels", new ResourceTemplate("blitz://projects/{project_id}/labels", { list: void 0 }), {
519
+ description: "Labels for a specific project.",
520
+ mimeType: "application/json"
521
+ }, async (uri, { project_id }) => {
522
+ const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/labels/`);
523
+ return { contents: [{
524
+ uri: uri.href,
525
+ mimeType: "application/json",
526
+ text: JSON.stringify(data, null, 2)
527
+ }] };
528
+ });
529
+ }
530
+
531
+ //#endregion
532
+ //#region src/resources/index.ts
533
+ function registerAllResources(server, client, config) {
534
+ registerProjectResources(server, client, config);
535
+ }
536
+
537
+ //#endregion
538
+ //#region src/server.ts
539
+ function createServer(config) {
540
+ const client = new BlitzClient(config);
541
+ const server = new McpServer({
542
+ name: "blitz-mcp",
543
+ version: "0.3.0"
544
+ });
545
+ registerAllTools(server, client, config);
546
+ registerAllResources(server, client, config);
547
+ return server;
548
+ }
549
+
550
+ //#endregion
551
+ export { loadConfig as n, createServer as t };
552
+ //# sourceMappingURL=server-BIEP9XVB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-BIEP9XVB.mjs","names":["missing: string[]","headers: Record<string, string>","members: Member[]","result: Record<string, unknown>"],"sources":["../src/config.ts","../src/client.ts","../src/tools/projects.ts","../src/tools/states.ts","../src/tools/labels.ts","../src/tools/search.ts","../src/tools/members.ts","../src/tools/work-items.ts","../src/tools/comments.ts","../src/tools/index.ts","../src/resources/projects.ts","../src/resources/index.ts","../src/server.ts"],"sourcesContent":["export interface BlitzConfig {\n apiUrl: string;\n apiKey: string;\n workspaceSlug: string;\n projectId?: string;\n}\n\nexport function loadConfig(overrides?: Partial<BlitzConfig>): BlitzConfig {\n const apiUrl = overrides?.apiUrl || process.env.BLITZ_API_URL;\n const apiKey = overrides?.apiKey || process.env.BLITZ_API_KEY;\n const workspaceSlug = overrides?.workspaceSlug || process.env.BLITZ_WORKSPACE_SLUG;\n const projectId = overrides?.projectId || process.env.BLITZ_PROJECT_ID;\n\n const missing: string[] = [];\n if (!apiUrl) missing.push(\"BLITZ_API_URL\");\n if (!apiKey) missing.push(\"BLITZ_API_KEY\");\n if (!workspaceSlug) missing.push(\"BLITZ_WORKSPACE_SLUG\");\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables: ${missing.join(\", \")}\\n\\n` +\n \"Please set the following:\\n\" +\n \" BLITZ_API_URL - Blitz instance URL (e.g. http://localhost:8000)\\n\" +\n \" BLITZ_API_KEY - API key (format: plane_api_<hex>)\\n\" +\n \" BLITZ_WORKSPACE_SLUG - Workspace slug\\n\" +\n \" BLITZ_PROJECT_ID - (Optional) Default project UUID\"\n );\n }\n\n return {\n apiUrl: apiUrl!.replace(/\\/+$/, \"\"),\n apiKey: apiKey!,\n workspaceSlug: workspaceSlug!,\n projectId: projectId || undefined,\n };\n}\n\nexport function resolveProjectId(projectId: string | undefined, config: BlitzConfig): string {\n const id = projectId || config.projectId;\n if (!id) {\n throw new Error(\n \"project_id is required. Either pass it as a parameter or set BLITZ_PROJECT_ID environment variable.\"\n );\n }\n return id;\n}\n","import type { BlitzConfig } from \"./config.js\";\n\nexport class BlitzClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: BlitzConfig) {\n this.baseUrl = `${config.apiUrl}/api/v1`;\n this.apiKey = config.apiKey;\n }\n\n private async request<T = unknown>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}/${path.replace(/^\\/+/, \"\")}`;\n\n const headers: Record<string, string> = {\n \"X-Api-Key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n };\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"Retry-After\");\n throw new Error(\n `Rate limit exceeded. ${retryAfter ? `Retry after ${retryAfter} seconds.` : \"Please wait before retrying.\"}`\n );\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n const data = await response.json();\n\n if (!response.ok) {\n const message =\n typeof data === \"object\" && data !== null\n ? JSON.stringify(data, null, 2)\n : String(data);\n throw new Error(`API error (${response.status}): ${message}`);\n }\n\n return data as T;\n }\n\n async get<T = unknown>(path: string): Promise<T> {\n return this.request<T>(\"GET\", path);\n }\n\n async post<T = unknown>(path: string, body: unknown): Promise<T> {\n return this.request<T>(\"POST\", path, body);\n }\n\n async patch<T = unknown>(path: string, body: unknown): Promise<T> {\n return this.request<T>(\"PATCH\", path, body);\n }\n\n async delete<T = unknown>(path: string): Promise<T> {\n return this.request<T>(\"DELETE\", path);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\n\nexport function registerProjectTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n server.tool(\n \"list-projects\",\n \"List all projects in the Blitz workspace. Returns project names, identifiers, and IDs.\",\n {},\n async () => {\n const data = await client.get(`workspaces/${config.workspaceSlug}/projects/`);\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\nimport { resolveProjectId } from \"../config.js\";\n\nexport function registerStateTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n server.tool(\n \"list-states\",\n \"List all workflow states for a project. Use this to find valid state IDs when creating or updating work items.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n },\n async ({ project_id }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.get(\n `workspaces/${config.workspaceSlug}/projects/${pid}/states/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\nimport { resolveProjectId } from \"../config.js\";\n\nexport function registerLabelTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n server.tool(\n \"list-labels\",\n \"List all labels for a project. Use this to find valid label IDs when creating or updating work items.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n },\n async ({ project_id }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.get(\n `workspaces/${config.workspaceSlug}/projects/${pid}/labels/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"create-label\",\n \"Create a new label in a project.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n name: z.string().min(1).max(255).describe(\"Label name\"),\n color: z.string().optional().describe(\"Hex color (e.g. #ff0000)\"),\n description: z.string().optional().describe(\"Label description\"),\n },\n async ({ project_id, ...body }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.post(\n `workspaces/${config.workspaceSlug}/projects/${pid}/labels/`,\n body\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\n\nexport function registerSearchTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n server.tool(\n \"search-work-items\",\n \"Search for work items (tasks/issues) across the workspace by text query. Searches in title and identifier.\",\n {\n query: z.string().min(1).describe(\"Search text\"),\n project_id: z.string().uuid().optional().describe(\"Optional project UUID to scope the search (defaults to BLITZ_PROJECT_ID if set)\"),\n },\n async ({ query, project_id }) => {\n const pid = project_id || config.projectId;\n let path = `workspaces/${config.workspaceSlug}/work-items/search/?search=${encodeURIComponent(query)}`;\n if (pid) {\n path += `&project_id=${pid}`;\n }\n const data = await client.get(path);\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\n\nexport interface Member {\n id: string;\n first_name: string;\n last_name: string;\n email: string;\n display_name: string;\n avatar: string;\n avatar_url: string;\n}\n\n// ---- Cache ----\n\nclass MemberCache {\n private cache = new Map<string, { members: Member[]; fetchedAt: number }>();\n private ttlMs: number;\n\n constructor(ttlMs = 5 * 60 * 1000) {\n this.ttlMs = ttlMs;\n }\n\n get(key: string): Member[] | null {\n const entry = this.cache.get(key);\n if (!entry || Date.now() - entry.fetchedAt > this.ttlMs) {\n if (entry) this.cache.delete(key);\n return null;\n }\n return entry.members;\n }\n\n set(key: string, members: Member[]): void {\n this.cache.set(key, { members, fetchedAt: Date.now() });\n }\n}\n\nconst cache = new MemberCache();\n\n// ---- Fetch helpers ----\n\nexport async function fetchProjectMembers(\n client: BlitzClient,\n workspaceSlug: string,\n projectId: string,\n): Promise<Member[]> {\n const key = `project:${projectId}`;\n const cached = cache.get(key);\n if (cached) return cached;\n\n const data = await client.get<Member[]>(\n `workspaces/${workspaceSlug}/projects/${projectId}/members/`\n );\n cache.set(key, data);\n return data;\n}\n\nasync function fetchWorkspaceMembers(\n client: BlitzClient,\n workspaceSlug: string,\n): Promise<Member[]> {\n const key = `workspace:${workspaceSlug}`;\n const cached = cache.get(key);\n if (cached) return cached;\n\n const data = await client.get<Member[]>(\n `workspaces/${workspaceSlug}/members/`\n );\n cache.set(key, data);\n return data;\n}\n\n// ---- Name resolution ----\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport async function resolveAssignees(\n values: string[],\n client: BlitzClient,\n workspaceSlug: string,\n projectId: string,\n): Promise<string[]> {\n if (values.every(v => UUID_RE.test(v))) return values;\n\n const members = await fetchProjectMembers(client, workspaceSlug, projectId);\n\n return values.map(value => {\n if (UUID_RE.test(value)) return value;\n\n const lower = value.toLowerCase();\n const match = members.find(m =>\n m.display_name.toLowerCase() === lower ||\n `${m.first_name} ${m.last_name}`.toLowerCase().trim() === lower ||\n m.email.toLowerCase() === lower\n );\n if (!match) {\n throw new Error(\n `Could not resolve assignee \"${value}\". Use list-members to see available members.`\n );\n }\n return match.id;\n });\n}\n\n// ---- Enrichment helper ----\n\nexport function enrichAssigneeNames(\n item: Record<string, unknown>,\n members: Member[],\n): Record<string, unknown> {\n const assigneeIds = item.assignees;\n if (!Array.isArray(assigneeIds) || assigneeIds.length === 0) return item;\n\n const memberMap = new Map(members.map(m => [m.id, m.display_name]));\n const assignees = assigneeIds.map(id => ({\n id,\n display_name: memberMap.get(id as string) ?? \"Unknown\",\n }));\n\n return { ...item, assignees };\n}\n\n// ---- Tool registration ----\n\nexport function registerMemberTools(\n server: McpServer,\n client: BlitzClient,\n config: BlitzConfig,\n) {\n const { workspaceSlug } = config;\n\n server.tool(\n \"list-members\",\n \"List members of a project or workspace. Use this to find user IDs and display names for assigning work items. You can also assign work items by display name directly.\",\n {\n project_id: z.string().uuid().optional().describe(\n \"Project UUID - lists project members. Omit to list workspace members (requires admin).\"\n ),\n },\n async ({ project_id }) => {\n let members: Member[];\n if (project_id || config.projectId) {\n const pid = project_id || config.projectId!;\n members = await fetchProjectMembers(client, workspaceSlug, pid);\n } else {\n members = await fetchWorkspaceMembers(client, workspaceSlug);\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(members, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\nimport { resolveProjectId } from \"../config.js\";\nimport { fetchProjectMembers, enrichAssigneeNames, resolveAssignees } from \"./members.js\";\n\nconst STATE_GROUPS = [\"backlog\", \"unstarted\", \"started\", \"completed\", \"cancelled\"] as const;\n\nconst SUMMARY_FIELDS = [\n \"id\", \"sequence_id\", \"name\", \"state\", \"priority\",\n \"assignees\", \"labels\", \"start_date\", \"target_date\",\n \"created_at\", \"updated_at\", \"completed_at\",\n] as const;\n\nconst HEAVY_FIELDS = [\n \"description_html\", \"description_binary\", \"description_stripped\",\n \"description\", \"issue_reactions\", \"issue_attachments\", \"issue_link\",\n];\n\nfunction buildListParams(opts: {\n cursor?: string;\n per_page?: number;\n order_by?: string;\n state_group?: string[];\n state_id?: string[];\n}): URLSearchParams {\n const params = new URLSearchParams();\n if (opts.cursor) params.set(\"cursor\", opts.cursor);\n if (opts.per_page) params.set(\"per_page\", String(opts.per_page));\n if (opts.order_by) params.set(\"order_by\", opts.order_by);\n if (opts.state_group?.length) params.set(\"state_group\", opts.state_group.join(\",\"));\n if (opts.state_id?.length) params.set(\"state\", opts.state_id.join(\",\"));\n return params;\n}\n\nfunction stripHeavyFields(item: Record<string, unknown>): Record<string, unknown> {\n const result = { ...item };\n for (const field of HEAVY_FIELDS) {\n delete result[field];\n }\n return result;\n}\n\nfunction pickSummaryFields(item: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const field of SUMMARY_FIELDS) {\n if (field in item) result[field] = item[field];\n }\n return result;\n}\n\nfunction mapBodyForApi(body: Record<string, unknown>): Record<string, unknown> {\n const mapped = { ...body };\n // API serializer expects \"state\" not \"state_id\"\n if (\"state_id\" in mapped) {\n mapped.state = mapped.state_id;\n delete mapped.state_id;\n }\n // API serializer expects \"parent\" not \"parent_id\"\n if (\"parent_id\" in mapped) {\n mapped.parent = mapped.parent_id;\n delete mapped.parent_id;\n }\n return mapped;\n}\n\nexport function registerWorkItemTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n const { workspaceSlug } = config;\n\n server.tool(\n \"list-work-items\",\n \"List work items in a project with filtering, pagination and sorting. Returns all fields except heavy description content.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n cursor: z.string().optional().describe(\"Pagination cursor from previous response\"),\n per_page: z.number().min(1).max(100).optional().describe(\"Items per page (default 20)\"),\n order_by: z.string().optional().describe(\"Sort field (e.g. -created_at, priority, state)\"),\n state_group: z.array(z.enum(STATE_GROUPS)).optional().describe(\"Filter by state group: backlog, unstarted, started, completed, cancelled\"),\n state_id: z.array(z.string().uuid()).optional().describe(\"Filter by specific state UUIDs\"),\n },\n async ({ project_id, cursor, per_page, order_by, state_group, state_id }) => {\n const pid = resolveProjectId(project_id, config);\n const params = buildListParams({ cursor, per_page, order_by, state_group, state_id });\n const query = params.toString();\n const path = `workspaces/${workspaceSlug}/projects/${pid}/work-items/${query ? `?${query}` : \"\"}`;\n const data = await client.get<Record<string, unknown>>(path);\n\n if (data.results && Array.isArray(data.results)) {\n const members = await fetchProjectMembers(client, workspaceSlug, pid);\n data.results = data.results.map((item: Record<string, unknown>) =>\n enrichAssigneeNames(stripHeavyFields(item), members)\n );\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"list-work-items-summary\",\n \"List work items with minimal fields only: name, state, priority, dates. Use this for overviews and large projects.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n cursor: z.string().optional().describe(\"Pagination cursor from previous response\"),\n per_page: z.number().min(1).max(100).optional().describe(\"Items per page (default 20)\"),\n order_by: z.string().optional().describe(\"Sort field (e.g. -created_at, priority, state)\"),\n state_group: z.array(z.enum(STATE_GROUPS)).optional().describe(\"Filter by state group: backlog, unstarted, started, completed, cancelled\"),\n state_id: z.array(z.string().uuid()).optional().describe(\"Filter by specific state UUIDs\"),\n },\n async ({ project_id, cursor, per_page, order_by, state_group, state_id }) => {\n const pid = resolveProjectId(project_id, config);\n const params = buildListParams({ cursor, per_page, order_by, state_group, state_id });\n const query = params.toString();\n const path = `workspaces/${workspaceSlug}/projects/${pid}/work-items/${query ? `?${query}` : \"\"}`;\n const data = await client.get<Record<string, unknown>>(path);\n\n if (data.results && Array.isArray(data.results)) {\n const members = await fetchProjectMembers(client, workspaceSlug, pid);\n data.results = data.results.map((item: Record<string, unknown>) =>\n enrichAssigneeNames(pickSummaryFields(item), members)\n );\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"get-work-item\",\n \"Get a work item by its human-readable identifier (e.g. BLZ-42). Returns full details.\",\n {\n identifier: z.string().min(1).describe(\"Work item identifier (e.g. PROJ-123)\"),\n },\n async ({ identifier }) => {\n const data = await client.get<Record<string, unknown>>(\n `workspaces/${workspaceSlug}/work-items/${identifier}/`\n );\n const projectId = (data.project_id ?? data.project) as string | undefined;\n if (projectId && Array.isArray(data.assignees)) {\n try {\n const members = await fetchProjectMembers(client, workspaceSlug, projectId);\n return {\n content: [{ type: \"text\", text: JSON.stringify(enrichAssigneeNames(data, members), null, 2) }],\n };\n } catch {\n // If member fetch fails, return unenriched\n }\n }\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"get-work-item-by-id\",\n \"Get a work item by its project ID and work item UUID.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.get<Record<string, unknown>>(\n `workspaces/${workspaceSlug}/projects/${pid}/work-items/${work_item_id}/`\n );\n const members = await fetchProjectMembers(client, workspaceSlug, pid);\n return {\n content: [{ type: \"text\", text: JSON.stringify(enrichAssigneeNames(data, members), null, 2) }],\n };\n }\n );\n\n server.tool(\n \"create-work-item\",\n \"Create a new work item (task/issue) in a project. Use list-projects to find project IDs, list-states for state IDs, and list-labels for label IDs.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n name: z.string().min(1).max(255).describe(\"Work item title\"),\n description_html: z.string().optional().describe(\"HTML description content\"),\n priority: z\n .enum([\"urgent\", \"high\", \"medium\", \"low\", \"none\"])\n .optional()\n .describe(\"Priority level\"),\n state_id: z.string().uuid().optional().describe(\"State UUID (use list-states to find valid IDs)\"),\n assignees: z\n .array(z.string())\n .optional()\n .describe(\"Array of user UUIDs or display names to assign. Use list-members to find valid names.\"),\n labels: z\n .array(z.string().uuid())\n .optional()\n .describe(\"Array of label UUIDs\"),\n start_date: z.string().optional().describe(\"Start date (YYYY-MM-DD)\"),\n target_date: z.string().optional().describe(\"Target date (YYYY-MM-DD)\"),\n parent_id: z.string().uuid().optional().describe(\"Parent work item UUID for sub-issues\"),\n },\n async ({ project_id, ...fields }) => {\n const pid = resolveProjectId(project_id, config);\n const resolvedFields = { ...fields } as Record<string, unknown>;\n if (fields.assignees?.length) {\n resolvedFields.assignees = await resolveAssignees(\n fields.assignees, client, workspaceSlug, pid\n );\n }\n const body = mapBodyForApi(resolvedFields);\n const data = await client.post(\n `workspaces/${workspaceSlug}/projects/${pid}/work-items/`,\n body\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"update-work-item\",\n \"Update an existing work item. Only provide fields you want to change.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n name: z.string().min(1).max(255).optional().describe(\"New title\"),\n description_html: z.string().optional().describe(\"New HTML description\"),\n priority: z\n .enum([\"urgent\", \"high\", \"medium\", \"low\", \"none\"])\n .optional()\n .describe(\"New priority level\"),\n state_id: z.string().uuid().optional().describe(\"New state UUID (use list-states to find valid IDs)\"),\n assignees: z\n .array(z.string())\n .optional()\n .describe(\"New assignee UUIDs or display names (replaces existing). Use list-members to find valid names.\"),\n labels: z\n .array(z.string().uuid())\n .optional()\n .describe(\"New label UUIDs (replaces existing)\"),\n start_date: z.string().nullable().optional().describe(\"Start date (YYYY-MM-DD) or null to clear\"),\n target_date: z.string().nullable().optional().describe(\"Target date (YYYY-MM-DD) or null to clear\"),\n },\n async ({ project_id, work_item_id, ...fields }) => {\n const pid = resolveProjectId(project_id, config);\n const resolvedFields = { ...fields } as Record<string, unknown>;\n if (fields.assignees?.length) {\n resolvedFields.assignees = await resolveAssignees(\n fields.assignees, client, workspaceSlug, pid\n );\n }\n const body = mapBodyForApi(resolvedFields);\n const data = await client.patch(\n `workspaces/${workspaceSlug}/projects/${pid}/work-items/${work_item_id}/`,\n body\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"delete-work-item\",\n \"Delete a work item. This action cannot be undone.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const pid = resolveProjectId(project_id, config);\n await client.delete(\n `workspaces/${workspaceSlug}/projects/${pid}/work-items/${work_item_id}/`\n );\n return {\n content: [{ type: \"text\", text: \"Work item deleted successfully.\" }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\nimport { resolveProjectId } from \"../config.js\";\n\nexport function registerCommentTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n server.tool(\n \"list-comments\",\n \"List all comments on a work item.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.get(\n `workspaces/${config.workspaceSlug}/projects/${pid}/work-items/${work_item_id}/comments/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"add-comment\",\n \"Add a comment to a work item.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n comment_html: z.string().min(1).describe(\"Comment content in HTML format\"),\n },\n async ({ project_id, work_item_id, comment_html }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.post(\n `workspaces/${config.workspaceSlug}/projects/${pid}/work-items/${work_item_id}/comments/`,\n { comment_html }\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"list-activities\",\n \"View the activity history (changelog) of a work item. Shows what changed, when, and by whom.\",\n {\n project_id: z.string().uuid().optional().describe(\"Project UUID (optional if BLITZ_PROJECT_ID is set)\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const pid = resolveProjectId(project_id, config);\n const data = await client.get(\n `workspaces/${config.workspaceSlug}/projects/${pid}/work-items/${work_item_id}/activities/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\nimport { registerProjectTools } from \"./projects.js\";\nimport { registerStateTools } from \"./states.js\";\nimport { registerLabelTools } from \"./labels.js\";\nimport { registerSearchTools } from \"./search.js\";\nimport { registerWorkItemTools } from \"./work-items.js\";\nimport { registerCommentTools } from \"./comments.js\";\nimport { registerMemberTools } from \"./members.js\";\n\nexport function registerAllTools(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n registerProjectTools(server, client, config);\n registerStateTools(server, client, config);\n registerLabelTools(server, client, config);\n registerSearchTools(server, client, config);\n registerWorkItemTools(server, client, config);\n registerCommentTools(server, client, config);\n registerMemberTools(server, client, config);\n}\n","import { McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\n\nexport function registerProjectResources(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n const { workspaceSlug } = config;\n\n server.registerResource(\n \"projects\",\n \"blitz://projects\",\n {\n description: \"List of all projects in the Blitz workspace with their identifiers, names, and IDs.\",\n mimeType: \"application/json\",\n },\n async (uri) => {\n const data = await client.get(`workspaces/${workspaceSlug}/projects/`);\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n );\n\n server.registerResource(\n \"project-states\",\n new ResourceTemplate(\"blitz://projects/{project_id}/states\", { list: undefined }),\n {\n description: \"Workflow states for a specific project.\",\n mimeType: \"application/json\",\n },\n async (uri, { project_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/states/`\n );\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n );\n\n server.registerResource(\n \"project-labels\",\n new ResourceTemplate(\"blitz://projects/{project_id}/labels\", { list: undefined }),\n {\n description: \"Labels for a specific project.\",\n mimeType: \"application/json\",\n },\n async (uri, { project_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/labels/`\n );\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport type { BlitzConfig } from \"../config.js\";\nimport { registerProjectResources } from \"./projects.js\";\n\nexport function registerAllResources(server: McpServer, client: BlitzClient, config: BlitzConfig) {\n registerProjectResources(server, client, config);\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { BlitzClient } from \"./client.js\";\nimport { registerAllTools } from \"./tools/index.js\";\nimport { registerAllResources } from \"./resources/index.js\";\nimport type { BlitzConfig } from \"./config.js\";\n\nexport function createServer(config: BlitzConfig) {\n const client = new BlitzClient(config);\n\n const server = new McpServer({\n name: \"blitz-mcp\",\n version: \"0.3.0\",\n });\n\n registerAllTools(server, client, config);\n registerAllResources(server, client, config);\n\n return server;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sixtynine-digital/blitz-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "license": "AGPL-3.0",
5
5
  "description": "MCP server for Blitz project management",
6
6
  "private": false,
@@ -1 +0,0 @@
1
- {"version":3,"file":"http-Dk8tMK0J.mjs","names":["params: Record<string, string>","createHttpServer","body","createServer"],"sources":["../src/http.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createServer as createHttpServer } from \"node:http\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport { loadConfig } from \"./config.js\";\nimport { createServer } from \"./server.js\";\n\ninterface SessionEntry {\n transport: StreamableHTTPServerTransport;\n server: ReturnType<typeof createServer>;\n}\n\nconst sessions = new Map<string, SessionEntry>();\n\nfunction parseBody(req: import(\"node:http\").IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = \"\";\n req.on(\"data\", (chunk: Buffer) => (data += chunk.toString()));\n req.on(\"end\", () => {\n try {\n resolve(JSON.parse(data));\n } catch {\n reject(new Error(\"Invalid JSON\"));\n }\n });\n req.on(\"error\", reject);\n });\n}\n\nfunction jsonResponse(\n res: import(\"node:http\").ServerResponse,\n status: number,\n body: unknown\n) {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n\nfunction parseQueryParams(url: string): Record<string, string> {\n const idx = url.indexOf(\"?\");\n if (idx === -1) return {};\n const params: Record<string, string> = {};\n const search = url.slice(idx + 1);\n for (const pair of search.split(\"&\")) {\n const [key, val] = pair.split(\"=\");\n if (key) params[decodeURIComponent(key)] = decodeURIComponent(val || \"\");\n }\n return params;\n}\n\nexport function startHttpServer() {\n const port = parseInt(process.env.BLITZ_MCP_PORT || \"3100\", 10);\n const apiUrl = process.env.BLITZ_API_URL;\n\n if (!apiUrl) {\n throw new Error(\"BLITZ_API_URL environment variable is required\");\n }\n\n const httpServer = createHttpServer(async (req, res) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n \"Content-Type, mcp-session-id\"\n );\n res.setHeader(\"Access-Control-Expose-Headers\", \"mcp-session-id\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const urlPath = (req.url || \"\").split(\"?\")[0];\n\n if (urlPath !== \"/mcp\") {\n jsonResponse(res, 404, { error: \"Not found\" });\n return;\n }\n\n if (req.method === \"POST\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n const body = await parseBody(req);\n await entry.transport.handleRequest(req, res, body);\n return;\n }\n\n const body = (await parseBody(req)) as Record<string, unknown>;\n\n if (!sessionId && isInitializeRequest(body)) {\n // Read per-user credentials from query params\n const query = parseQueryParams(req.url || \"\");\n const apiKey = query.api_key || process.env.BLITZ_API_KEY;\n const workspaceSlug = query.workspace || process.env.BLITZ_WORKSPACE_SLUG;\n\n if (!apiKey) {\n jsonResponse(res, 401, {\n error: \"API key required. Add ?api_key=plane_api_XXX to the URL\",\n });\n return;\n }\n\n if (!workspaceSlug) {\n jsonResponse(res, 400, {\n error:\n \"Workspace slug required. Add ?workspace=YOUR_SLUG to the URL\",\n });\n return;\n }\n\n const config = loadConfig({\n apiUrl,\n apiKey,\n workspaceSlug,\n });\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (id) => {\n sessions.set(id, { transport, server });\n },\n });\n\n transport.onclose = () => {\n if (transport.sessionId) {\n sessions.delete(transport.sessionId);\n }\n };\n\n const server = createServer(config);\n await server.connect(transport);\n await transport.handleRequest(req, res, body);\n return;\n }\n\n jsonResponse(res, 400, {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Invalid session\" },\n id: null,\n });\n return;\n }\n\n if (req.method === \"GET\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string;\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n await entry.transport.handleRequest(req, res);\n } else {\n jsonResponse(res, 400, { error: \"Invalid session\" });\n }\n return;\n }\n\n if (req.method === \"DELETE\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string;\n if (sessionId && sessions.has(sessionId)) {\n const entry = sessions.get(sessionId)!;\n await entry.transport.handleRequest(req, res);\n } else {\n jsonResponse(res, 400, { error: \"Invalid session\" });\n }\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n });\n\n httpServer.listen(port, \"0.0.0.0\", () => {\n console.log(`Blitz MCP HTTP server running on port ${port}`);\n console.log(`Endpoint: http://0.0.0.0:${port}/mcp`);\n });\n\n process.on(\"SIGINT\", () => {\n httpServer.close();\n process.exit(0);\n });\n\n process.on(\"SIGTERM\", () => {\n httpServer.close();\n process.exit(0);\n });\n}\n"],"mappings":"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"}
@@ -1,340 +0,0 @@
1
- #!/usr/bin/env node
2
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { z } from "zod";
4
-
5
- //#region src/config.ts
6
- function loadConfig(overrides) {
7
- const apiUrl = overrides?.apiUrl || process.env.BLITZ_API_URL;
8
- const apiKey = overrides?.apiKey || process.env.BLITZ_API_KEY;
9
- const workspaceSlug = overrides?.workspaceSlug || process.env.BLITZ_WORKSPACE_SLUG;
10
- const missing = [];
11
- if (!apiUrl) missing.push("BLITZ_API_URL");
12
- if (!apiKey) missing.push("BLITZ_API_KEY");
13
- if (!workspaceSlug) missing.push("BLITZ_WORKSPACE_SLUG");
14
- if (missing.length > 0) throw new Error(`Missing required environment variables: ${missing.join(", ")}\n\nPlease set the following:
15
- BLITZ_API_URL - Blitz instance URL (e.g. http://localhost:8000)
16
- BLITZ_API_KEY - API key (format: plane_api_<hex>)
17
- BLITZ_WORKSPACE_SLUG - Workspace slug`);
18
- return {
19
- apiUrl: apiUrl.replace(/\/+$/, ""),
20
- apiKey,
21
- workspaceSlug
22
- };
23
- }
24
-
25
- //#endregion
26
- //#region src/client.ts
27
- var BlitzClient = class {
28
- baseUrl;
29
- apiKey;
30
- constructor(config) {
31
- this.baseUrl = `${config.apiUrl}/api/v1`;
32
- this.apiKey = config.apiKey;
33
- }
34
- async request(method, path, body) {
35
- const url = `${this.baseUrl}/${path.replace(/^\/+/, "")}`;
36
- const headers = {
37
- "X-Api-Key": this.apiKey,
38
- "Content-Type": "application/json"
39
- };
40
- const response = await fetch(url, {
41
- method,
42
- headers,
43
- body: body ? JSON.stringify(body) : void 0
44
- });
45
- if (response.status === 429) {
46
- const retryAfter = response.headers.get("Retry-After");
47
- throw new Error(`Rate limit exceeded. ${retryAfter ? `Retry after ${retryAfter} seconds.` : "Please wait before retrying."}`);
48
- }
49
- if (response.status === 204) return;
50
- const data = await response.json();
51
- if (!response.ok) {
52
- const message = typeof data === "object" && data !== null ? JSON.stringify(data, null, 2) : String(data);
53
- throw new Error(`API error (${response.status}): ${message}`);
54
- }
55
- return data;
56
- }
57
- async get(path) {
58
- return this.request("GET", path);
59
- }
60
- async post(path, body) {
61
- return this.request("POST", path, body);
62
- }
63
- async patch(path, body) {
64
- return this.request("PATCH", path, body);
65
- }
66
- async delete(path) {
67
- return this.request("DELETE", path);
68
- }
69
- };
70
-
71
- //#endregion
72
- //#region src/tools/projects.ts
73
- function registerProjectTools(server, client, workspaceSlug) {
74
- server.tool("list-projects", "List all projects in the Blitz workspace. Returns project names, identifiers, and IDs.", {}, async () => {
75
- const data = await client.get(`workspaces/${workspaceSlug}/projects/`);
76
- return { content: [{
77
- type: "text",
78
- text: JSON.stringify(data, null, 2)
79
- }] };
80
- });
81
- }
82
-
83
- //#endregion
84
- //#region src/tools/states.ts
85
- function registerStateTools(server, client, workspaceSlug) {
86
- server.tool("list-states", "List all workflow states for a project. Use this to find valid state IDs when creating or updating work items.", { project_id: z.string().uuid().describe("Project UUID") }, async ({ project_id }) => {
87
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/states/`);
88
- return { content: [{
89
- type: "text",
90
- text: JSON.stringify(data, null, 2)
91
- }] };
92
- });
93
- }
94
-
95
- //#endregion
96
- //#region src/tools/labels.ts
97
- function registerLabelTools(server, client, workspaceSlug) {
98
- server.tool("list-labels", "List all labels for a project. Use this to find valid label IDs when creating or updating work items.", { project_id: z.string().uuid().describe("Project UUID") }, async ({ project_id }) => {
99
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/labels/`);
100
- return { content: [{
101
- type: "text",
102
- text: JSON.stringify(data, null, 2)
103
- }] };
104
- });
105
- server.tool("create-label", "Create a new label in a project.", {
106
- project_id: z.string().uuid().describe("Project UUID"),
107
- name: z.string().min(1).max(255).describe("Label name"),
108
- color: z.string().optional().describe("Hex color (e.g. #ff0000)"),
109
- description: z.string().optional().describe("Label description")
110
- }, async ({ project_id,...body }) => {
111
- const data = await client.post(`workspaces/${workspaceSlug}/projects/${project_id}/labels/`, body);
112
- return { content: [{
113
- type: "text",
114
- text: JSON.stringify(data, null, 2)
115
- }] };
116
- });
117
- }
118
-
119
- //#endregion
120
- //#region src/tools/search.ts
121
- function registerSearchTools(server, client, workspaceSlug) {
122
- server.tool("search-work-items", "Search for work items (tasks/issues) across the workspace by text query. Searches in title and identifier.", {
123
- query: z.string().min(1).describe("Search text"),
124
- project_id: z.string().uuid().optional().describe("Optional project UUID to scope the search")
125
- }, async ({ query, project_id }) => {
126
- let path = `workspaces/${workspaceSlug}/work-items/search/?search=${encodeURIComponent(query)}`;
127
- if (project_id) path += `&project_id=${project_id}`;
128
- const data = await client.get(path);
129
- return { content: [{
130
- type: "text",
131
- text: JSON.stringify(data, null, 2)
132
- }] };
133
- });
134
- }
135
-
136
- //#endregion
137
- //#region src/tools/work-items.ts
138
- function registerWorkItemTools(server, client, workspaceSlug) {
139
- server.tool("list-work-items", "List work items (tasks/issues) in a project with pagination and sorting.", {
140
- project_id: z.string().uuid().describe("Project UUID"),
141
- cursor: z.string().optional().describe("Pagination cursor from previous response"),
142
- per_page: z.number().min(1).max(100).optional().describe("Items per page (default 20)"),
143
- order_by: z.string().optional().describe("Sort field (e.g. -created_at, priority, state)")
144
- }, async ({ project_id, cursor, per_page, order_by }) => {
145
- const params = new URLSearchParams();
146
- if (cursor) params.set("cursor", cursor);
147
- if (per_page) params.set("per_page", String(per_page));
148
- if (order_by) params.set("order_by", order_by);
149
- const query = params.toString();
150
- const path = `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${query ? `?${query}` : ""}`;
151
- const data = await client.get(path);
152
- return { content: [{
153
- type: "text",
154
- text: JSON.stringify(data, null, 2)
155
- }] };
156
- });
157
- server.tool("get-work-item", "Get a work item by its human-readable identifier (e.g. BLZ-42). Returns full details.", { identifier: z.string().min(1).describe("Work item identifier (e.g. PROJ-123)") }, async ({ identifier }) => {
158
- const data = await client.get(`workspaces/${workspaceSlug}/work-items/${identifier}/`);
159
- return { content: [{
160
- type: "text",
161
- text: JSON.stringify(data, null, 2)
162
- }] };
163
- });
164
- server.tool("get-work-item-by-id", "Get a work item by its project ID and work item UUID.", {
165
- project_id: z.string().uuid().describe("Project UUID"),
166
- work_item_id: z.string().uuid().describe("Work item UUID")
167
- }, async ({ project_id, work_item_id }) => {
168
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/`);
169
- return { content: [{
170
- type: "text",
171
- text: JSON.stringify(data, null, 2)
172
- }] };
173
- });
174
- server.tool("create-work-item", "Create a new work item (task/issue) in a project. Use list-projects to find project IDs, list-states for state IDs, and list-labels for label IDs.", {
175
- project_id: z.string().uuid().describe("Project UUID"),
176
- name: z.string().min(1).max(255).describe("Work item title"),
177
- description_html: z.string().optional().describe("HTML description content"),
178
- priority: z.enum([
179
- "urgent",
180
- "high",
181
- "medium",
182
- "low",
183
- "none"
184
- ]).optional().describe("Priority level"),
185
- state_id: z.string().uuid().optional().describe("State UUID (use list-states to find valid IDs)"),
186
- assignees: z.array(z.string().uuid()).optional().describe("Array of user UUIDs to assign"),
187
- labels: z.array(z.string().uuid()).optional().describe("Array of label UUIDs"),
188
- start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
189
- target_date: z.string().optional().describe("Target date (YYYY-MM-DD)"),
190
- parent_id: z.string().uuid().optional().describe("Parent work item UUID for sub-issues")
191
- }, async ({ project_id,...body }) => {
192
- const data = await client.post(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/`, body);
193
- return { content: [{
194
- type: "text",
195
- text: JSON.stringify(data, null, 2)
196
- }] };
197
- });
198
- server.tool("update-work-item", "Update an existing work item. Only provide fields you want to change.", {
199
- project_id: z.string().uuid().describe("Project UUID"),
200
- work_item_id: z.string().uuid().describe("Work item UUID"),
201
- name: z.string().min(1).max(255).optional().describe("New title"),
202
- description_html: z.string().optional().describe("New HTML description"),
203
- priority: z.enum([
204
- "urgent",
205
- "high",
206
- "medium",
207
- "low",
208
- "none"
209
- ]).optional().describe("New priority level"),
210
- state_id: z.string().uuid().optional().describe("New state UUID"),
211
- assignees: z.array(z.string().uuid()).optional().describe("New assignee UUIDs (replaces existing)"),
212
- labels: z.array(z.string().uuid()).optional().describe("New label UUIDs (replaces existing)"),
213
- start_date: z.string().nullable().optional().describe("Start date (YYYY-MM-DD) or null to clear"),
214
- target_date: z.string().nullable().optional().describe("Target date (YYYY-MM-DD) or null to clear")
215
- }, async ({ project_id, work_item_id,...body }) => {
216
- const data = await client.patch(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/`, body);
217
- return { content: [{
218
- type: "text",
219
- text: JSON.stringify(data, null, 2)
220
- }] };
221
- });
222
- server.tool("delete-work-item", "Delete a work item. This action cannot be undone.", {
223
- project_id: z.string().uuid().describe("Project UUID"),
224
- work_item_id: z.string().uuid().describe("Work item UUID")
225
- }, async ({ project_id, work_item_id }) => {
226
- await client.delete(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/`);
227
- return { content: [{
228
- type: "text",
229
- text: "Work item deleted successfully."
230
- }] };
231
- });
232
- }
233
-
234
- //#endregion
235
- //#region src/tools/comments.ts
236
- function registerCommentTools(server, client, workspaceSlug) {
237
- server.tool("list-comments", "List all comments on a work item.", {
238
- project_id: z.string().uuid().describe("Project UUID"),
239
- work_item_id: z.string().uuid().describe("Work item UUID")
240
- }, async ({ project_id, work_item_id }) => {
241
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/comments/`);
242
- return { content: [{
243
- type: "text",
244
- text: JSON.stringify(data, null, 2)
245
- }] };
246
- });
247
- server.tool("add-comment", "Add a comment to a work item.", {
248
- project_id: z.string().uuid().describe("Project UUID"),
249
- work_item_id: z.string().uuid().describe("Work item UUID"),
250
- comment_html: z.string().min(1).describe("Comment content in HTML format")
251
- }, async ({ project_id, work_item_id, comment_html }) => {
252
- const data = await client.post(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/comments/`, { comment_html });
253
- return { content: [{
254
- type: "text",
255
- text: JSON.stringify(data, null, 2)
256
- }] };
257
- });
258
- server.tool("list-activities", "View the activity history (changelog) of a work item. Shows what changed, when, and by whom.", {
259
- project_id: z.string().uuid().describe("Project UUID"),
260
- work_item_id: z.string().uuid().describe("Work item UUID")
261
- }, async ({ project_id, work_item_id }) => {
262
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/activities/`);
263
- return { content: [{
264
- type: "text",
265
- text: JSON.stringify(data, null, 2)
266
- }] };
267
- });
268
- }
269
-
270
- //#endregion
271
- //#region src/tools/index.ts
272
- function registerAllTools(server, client, workspaceSlug) {
273
- registerProjectTools(server, client, workspaceSlug);
274
- registerStateTools(server, client, workspaceSlug);
275
- registerLabelTools(server, client, workspaceSlug);
276
- registerSearchTools(server, client, workspaceSlug);
277
- registerWorkItemTools(server, client, workspaceSlug);
278
- registerCommentTools(server, client, workspaceSlug);
279
- }
280
-
281
- //#endregion
282
- //#region src/resources/projects.ts
283
- function registerProjectResources(server, client, workspaceSlug) {
284
- server.registerResource("projects", "blitz://projects", {
285
- description: "List of all projects in the Blitz workspace with their identifiers, names, and IDs.",
286
- mimeType: "application/json"
287
- }, async (uri) => {
288
- const data = await client.get(`workspaces/${workspaceSlug}/projects/`);
289
- return { contents: [{
290
- uri: uri.href,
291
- mimeType: "application/json",
292
- text: JSON.stringify(data, null, 2)
293
- }] };
294
- });
295
- server.registerResource("project-states", new ResourceTemplate("blitz://projects/{project_id}/states", { list: void 0 }), {
296
- description: "Workflow states for a specific project.",
297
- mimeType: "application/json"
298
- }, async (uri, { project_id }) => {
299
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/states/`);
300
- return { contents: [{
301
- uri: uri.href,
302
- mimeType: "application/json",
303
- text: JSON.stringify(data, null, 2)
304
- }] };
305
- });
306
- server.registerResource("project-labels", new ResourceTemplate("blitz://projects/{project_id}/labels", { list: void 0 }), {
307
- description: "Labels for a specific project.",
308
- mimeType: "application/json"
309
- }, async (uri, { project_id }) => {
310
- const data = await client.get(`workspaces/${workspaceSlug}/projects/${project_id}/labels/`);
311
- return { contents: [{
312
- uri: uri.href,
313
- mimeType: "application/json",
314
- text: JSON.stringify(data, null, 2)
315
- }] };
316
- });
317
- }
318
-
319
- //#endregion
320
- //#region src/resources/index.ts
321
- function registerAllResources(server, client, workspaceSlug) {
322
- registerProjectResources(server, client, workspaceSlug);
323
- }
324
-
325
- //#endregion
326
- //#region src/server.ts
327
- function createServer(config) {
328
- const client = new BlitzClient(config);
329
- const server = new McpServer({
330
- name: "blitz-mcp",
331
- version: "0.1.1"
332
- });
333
- registerAllTools(server, client, config.workspaceSlug);
334
- registerAllResources(server, client, config.workspaceSlug);
335
- return server;
336
- }
337
-
338
- //#endregion
339
- export { loadConfig as n, createServer as t };
340
- //# sourceMappingURL=server-pwmkXBAV.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server-pwmkXBAV.mjs","names":["missing: string[]","headers: Record<string, string>"],"sources":["../src/config.ts","../src/client.ts","../src/tools/projects.ts","../src/tools/states.ts","../src/tools/labels.ts","../src/tools/search.ts","../src/tools/work-items.ts","../src/tools/comments.ts","../src/tools/index.ts","../src/resources/projects.ts","../src/resources/index.ts","../src/server.ts"],"sourcesContent":["export interface BlitzConfig {\n apiUrl: string;\n apiKey: string;\n workspaceSlug: string;\n}\n\nexport function loadConfig(overrides?: Partial<BlitzConfig>): BlitzConfig {\n const apiUrl = overrides?.apiUrl || process.env.BLITZ_API_URL;\n const apiKey = overrides?.apiKey || process.env.BLITZ_API_KEY;\n const workspaceSlug = overrides?.workspaceSlug || process.env.BLITZ_WORKSPACE_SLUG;\n\n const missing: string[] = [];\n if (!apiUrl) missing.push(\"BLITZ_API_URL\");\n if (!apiKey) missing.push(\"BLITZ_API_KEY\");\n if (!workspaceSlug) missing.push(\"BLITZ_WORKSPACE_SLUG\");\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables: ${missing.join(\", \")}\\n\\n` +\n \"Please set the following:\\n\" +\n \" BLITZ_API_URL - Blitz instance URL (e.g. http://localhost:8000)\\n\" +\n \" BLITZ_API_KEY - API key (format: plane_api_<hex>)\\n\" +\n \" BLITZ_WORKSPACE_SLUG - Workspace slug\"\n );\n }\n\n return {\n apiUrl: apiUrl!.replace(/\\/+$/, \"\"),\n apiKey: apiKey!,\n workspaceSlug: workspaceSlug!,\n };\n}\n","import type { BlitzConfig } from \"./config.js\";\n\nexport class BlitzClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: BlitzConfig) {\n this.baseUrl = `${config.apiUrl}/api/v1`;\n this.apiKey = config.apiKey;\n }\n\n private async request<T = unknown>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}/${path.replace(/^\\/+/, \"\")}`;\n\n const headers: Record<string, string> = {\n \"X-Api-Key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n };\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"Retry-After\");\n throw new Error(\n `Rate limit exceeded. ${retryAfter ? `Retry after ${retryAfter} seconds.` : \"Please wait before retrying.\"}`\n );\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n const data = await response.json();\n\n if (!response.ok) {\n const message =\n typeof data === \"object\" && data !== null\n ? JSON.stringify(data, null, 2)\n : String(data);\n throw new Error(`API error (${response.status}): ${message}`);\n }\n\n return data as T;\n }\n\n async get<T = unknown>(path: string): Promise<T> {\n return this.request<T>(\"GET\", path);\n }\n\n async post<T = unknown>(path: string, body: unknown): Promise<T> {\n return this.request<T>(\"POST\", path, body);\n }\n\n async patch<T = unknown>(path: string, body: unknown): Promise<T> {\n return this.request<T>(\"PATCH\", path, body);\n }\n\n async delete<T = unknown>(path: string): Promise<T> {\n return this.request<T>(\"DELETE\", path);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerProjectTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.tool(\n \"list-projects\",\n \"List all projects in the Blitz workspace. Returns project names, identifiers, and IDs.\",\n {},\n async () => {\n const data = await client.get(`workspaces/${workspaceSlug}/projects/`);\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerStateTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.tool(\n \"list-states\",\n \"List all workflow states for a project. Use this to find valid state IDs when creating or updating work items.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n },\n async ({ project_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/states/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerLabelTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.tool(\n \"list-labels\",\n \"List all labels for a project. Use this to find valid label IDs when creating or updating work items.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n },\n async ({ project_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/labels/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"create-label\",\n \"Create a new label in a project.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n name: z.string().min(1).max(255).describe(\"Label name\"),\n color: z.string().optional().describe(\"Hex color (e.g. #ff0000)\"),\n description: z.string().optional().describe(\"Label description\"),\n },\n async ({ project_id, ...body }) => {\n const data = await client.post(\n `workspaces/${workspaceSlug}/projects/${project_id}/labels/`,\n body\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerSearchTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.tool(\n \"search-work-items\",\n \"Search for work items (tasks/issues) across the workspace by text query. Searches in title and identifier.\",\n {\n query: z.string().min(1).describe(\"Search text\"),\n project_id: z.string().uuid().optional().describe(\"Optional project UUID to scope the search\"),\n },\n async ({ query, project_id }) => {\n let path = `workspaces/${workspaceSlug}/work-items/search/?search=${encodeURIComponent(query)}`;\n if (project_id) {\n path += `&project_id=${project_id}`;\n }\n const data = await client.get(path);\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerWorkItemTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.tool(\n \"list-work-items\",\n \"List work items (tasks/issues) in a project with pagination and sorting.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n cursor: z.string().optional().describe(\"Pagination cursor from previous response\"),\n per_page: z.number().min(1).max(100).optional().describe(\"Items per page (default 20)\"),\n order_by: z.string().optional().describe(\"Sort field (e.g. -created_at, priority, state)\"),\n },\n async ({ project_id, cursor, per_page, order_by }) => {\n const params = new URLSearchParams();\n if (cursor) params.set(\"cursor\", cursor);\n if (per_page) params.set(\"per_page\", String(per_page));\n if (order_by) params.set(\"order_by\", order_by);\n\n const query = params.toString();\n const path = `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${query ? `?${query}` : \"\"}`;\n const data = await client.get(path);\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"get-work-item\",\n \"Get a work item by its human-readable identifier (e.g. BLZ-42). Returns full details.\",\n {\n identifier: z.string().min(1).describe(\"Work item identifier (e.g. PROJ-123)\"),\n },\n async ({ identifier }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/work-items/${identifier}/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"get-work-item-by-id\",\n \"Get a work item by its project ID and work item UUID.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"create-work-item\",\n \"Create a new work item (task/issue) in a project. Use list-projects to find project IDs, list-states for state IDs, and list-labels for label IDs.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n name: z.string().min(1).max(255).describe(\"Work item title\"),\n description_html: z.string().optional().describe(\"HTML description content\"),\n priority: z\n .enum([\"urgent\", \"high\", \"medium\", \"low\", \"none\"])\n .optional()\n .describe(\"Priority level\"),\n state_id: z.string().uuid().optional().describe(\"State UUID (use list-states to find valid IDs)\"),\n assignees: z\n .array(z.string().uuid())\n .optional()\n .describe(\"Array of user UUIDs to assign\"),\n labels: z\n .array(z.string().uuid())\n .optional()\n .describe(\"Array of label UUIDs\"),\n start_date: z.string().optional().describe(\"Start date (YYYY-MM-DD)\"),\n target_date: z.string().optional().describe(\"Target date (YYYY-MM-DD)\"),\n parent_id: z.string().uuid().optional().describe(\"Parent work item UUID for sub-issues\"),\n },\n async ({ project_id, ...body }) => {\n const data = await client.post(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/`,\n body\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"update-work-item\",\n \"Update an existing work item. Only provide fields you want to change.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n name: z.string().min(1).max(255).optional().describe(\"New title\"),\n description_html: z.string().optional().describe(\"New HTML description\"),\n priority: z\n .enum([\"urgent\", \"high\", \"medium\", \"low\", \"none\"])\n .optional()\n .describe(\"New priority level\"),\n state_id: z.string().uuid().optional().describe(\"New state UUID\"),\n assignees: z\n .array(z.string().uuid())\n .optional()\n .describe(\"New assignee UUIDs (replaces existing)\"),\n labels: z\n .array(z.string().uuid())\n .optional()\n .describe(\"New label UUIDs (replaces existing)\"),\n start_date: z.string().nullable().optional().describe(\"Start date (YYYY-MM-DD) or null to clear\"),\n target_date: z.string().nullable().optional().describe(\"Target date (YYYY-MM-DD) or null to clear\"),\n },\n async ({ project_id, work_item_id, ...body }) => {\n const data = await client.patch(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/`,\n body\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"delete-work-item\",\n \"Delete a work item. This action cannot be undone.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n await client.delete(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/`\n );\n return {\n content: [{ type: \"text\", text: \"Work item deleted successfully.\" }],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerCommentTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.tool(\n \"list-comments\",\n \"List all comments on a work item.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/comments/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"add-comment\",\n \"Add a comment to a work item.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n comment_html: z.string().min(1).describe(\"Comment content in HTML format\"),\n },\n async ({ project_id, work_item_id, comment_html }) => {\n const data = await client.post(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/comments/`,\n { comment_html }\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"list-activities\",\n \"View the activity history (changelog) of a work item. Shows what changed, when, and by whom.\",\n {\n project_id: z.string().uuid().describe(\"Project UUID\"),\n work_item_id: z.string().uuid().describe(\"Work item UUID\"),\n },\n async ({ project_id, work_item_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/work-items/${work_item_id}/activities/`\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport { registerProjectTools } from \"./projects.js\";\nimport { registerStateTools } from \"./states.js\";\nimport { registerLabelTools } from \"./labels.js\";\nimport { registerSearchTools } from \"./search.js\";\nimport { registerWorkItemTools } from \"./work-items.js\";\nimport { registerCommentTools } from \"./comments.js\";\n\nexport function registerAllTools(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n registerProjectTools(server, client, workspaceSlug);\n registerStateTools(server, client, workspaceSlug);\n registerLabelTools(server, client, workspaceSlug);\n registerSearchTools(server, client, workspaceSlug);\n registerWorkItemTools(server, client, workspaceSlug);\n registerCommentTools(server, client, workspaceSlug);\n}\n","import { McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\n\nexport function registerProjectResources(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n server.registerResource(\n \"projects\",\n \"blitz://projects\",\n {\n description: \"List of all projects in the Blitz workspace with their identifiers, names, and IDs.\",\n mimeType: \"application/json\",\n },\n async (uri) => {\n const data = await client.get(`workspaces/${workspaceSlug}/projects/`);\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n );\n\n server.registerResource(\n \"project-states\",\n new ResourceTemplate(\"blitz://projects/{project_id}/states\", { list: undefined }),\n {\n description: \"Workflow states for a specific project.\",\n mimeType: \"application/json\",\n },\n async (uri, { project_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/states/`\n );\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n );\n\n server.registerResource(\n \"project-labels\",\n new ResourceTemplate(\"blitz://projects/{project_id}/labels\", { list: undefined }),\n {\n description: \"Labels for a specific project.\",\n mimeType: \"application/json\",\n },\n async (uri, { project_id }) => {\n const data = await client.get(\n `workspaces/${workspaceSlug}/projects/${project_id}/labels/`\n );\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlitzClient } from \"../client.js\";\nimport { registerProjectResources } from \"./projects.js\";\n\nexport function registerAllResources(server: McpServer, client: BlitzClient, workspaceSlug: string) {\n registerProjectResources(server, client, workspaceSlug);\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { BlitzClient } from \"./client.js\";\nimport { registerAllTools } from \"./tools/index.js\";\nimport { registerAllResources } from \"./resources/index.js\";\nimport type { BlitzConfig } from \"./config.js\";\n\nexport function createServer(config: BlitzConfig) {\n const client = new BlitzClient(config);\n\n const server = new McpServer({\n name: \"blitz-mcp\",\n version: \"0.1.1\",\n });\n\n registerAllTools(server, client, config.workspaceSlug);\n registerAllResources(server, client, config.workspaceSlug);\n\n return server;\n}\n"],"mappings":"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"}