brioright-mcp 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +100 -2
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -54,7 +54,11 @@ async function call(method, path, data, overrideApiKey) {
54
54
  })
55
55
 
56
56
  try {
57
- const res = await api({ method, url: path, data })
57
+ const reqConfig = { method, url: path };
58
+ if (data !== null && data !== undefined) {
59
+ reqConfig.data = data;
60
+ }
61
+ const res = await api(reqConfig);
58
62
  return res.data.data
59
63
  } catch (err) {
60
64
  const msg = err.response?.data?.message || err.message || 'Unknown error'
@@ -151,6 +155,41 @@ function buildServer() {
151
155
  }
152
156
  )
153
157
 
158
+ // ── bulk_create_tasks ─────────────────────────────────────────────────────
159
+ server.tool('bulk_create_tasks', 'Create multiple tasks at once in a Brioright project',
160
+ {
161
+ projectId: z.string().describe('Project ID'),
162
+ tasks: z.array(z.object({
163
+ title: z.string().describe('Task title'),
164
+ description: z.string().optional(),
165
+ status: z.enum(['todo', 'in_progress', 'in_review', 'done']).optional().default('todo'),
166
+ priority: z.enum(['low', 'medium', 'high', 'urgent']).optional().default('medium'),
167
+ dueDate: z.string().optional().describe('ISO date string e.g. 2026-03-15'),
168
+ assigneeId: z.string().optional(),
169
+ parentTaskId: z.string().optional(),
170
+ tags: z.array(z.string()).optional()
171
+ })).describe('Array of tasks to create'),
172
+ workspaceId: z.string().optional(),
173
+ apiKey: z.string().optional().describe('Brioright API Key')
174
+ },
175
+ async ({ projectId, tasks, workspaceId, apiKey }) => {
176
+ const ws = workspaceId || DEFAULT_WORKSPACE
177
+ if (!ws) throw new Error('workspaceId is required')
178
+
179
+ // Format dates
180
+ const formattedTasks = tasks.map(t => ({
181
+ ...t,
182
+ dueDate: t.dueDate ? new Date(t.dueDate).toISOString() : undefined
183
+ }))
184
+
185
+ const data = await call('POST', `/workspaces/${ws}/projects/${projectId}/tasks/bulk`, {
186
+ tasks: formattedTasks
187
+ }, apiKey)
188
+
189
+ return { content: [{ type: 'text', text: `✅ Successfully created ${data.count} tasks!` }] }
190
+ }
191
+ )
192
+
154
193
  // ── update_task ───────────────────────────────────────────────────────────
155
194
  server.tool('update_task', 'Update fields on an existing task',
156
195
  {
@@ -171,7 +210,7 @@ function buildServer() {
171
210
  if (updates.dueDate) updates.dueDate = new Date(updates.dueDate).toISOString()
172
211
  const data = await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, updates, apiKey)
173
212
  const task = data.task || data
174
- return { content: [{ type: 'text', text: `✅ Task updated!\n\n${JSON.stringify({ id: task.id, title: task.title, status: task.status, priority: task.priority }, null, 2)}` }] }
213
+ return { content: [{ type: 'text', text: `✅ Task updated!\n\n${JSON.stringify({ id: task.id, title: task.title, status: task.status, priority: task.priority, description: task.description, dueDate: task.dueDate }, null, 2)}` }] }
175
214
  }
176
215
  )
177
216
 
@@ -227,6 +266,65 @@ function buildServer() {
227
266
  }
228
267
  )
229
268
 
269
+ // ── add_comment ───────────────────────────────────────────────────────────
270
+ server.tool('add_comment', 'Add a comment to a task',
271
+ {
272
+ taskId: z.string(),
273
+ text: z.string().describe('The content of the comment'),
274
+ workspaceId: z.string().optional(),
275
+ apiKey: z.string().optional().describe('Brioright API Key')
276
+ },
277
+ async ({ taskId, text, workspaceId, apiKey }) => {
278
+ const ws = workspaceId || DEFAULT_WORKSPACE
279
+ if (!ws) throw new Error('workspaceId is required')
280
+ const data = await call('POST', `/workspaces/${ws}/tasks/${taskId}/comments`, { text }, apiKey)
281
+ const comment = data.comment || data
282
+ return { content: [{ type: 'text', text: `✅ Comment added!\n\n${JSON.stringify(comment, null, 2)}` }] }
283
+ }
284
+ )
285
+
286
+ // ── get_task_comments ─────────────────────────────────────────────────────
287
+ server.tool('get_task_comments', 'Get all comments for a given task',
288
+ {
289
+ taskId: z.string(),
290
+ workspaceId: z.string().optional(),
291
+ apiKey: z.string().optional().describe('Brioright API Key')
292
+ },
293
+ async ({ taskId, workspaceId, apiKey }) => {
294
+ const ws = workspaceId || DEFAULT_WORKSPACE
295
+ if (!ws) throw new Error('workspaceId is required')
296
+ const data = await call('GET', `/workspaces/${ws}/tasks/${taskId}/comments`, null, apiKey)
297
+ const comments = data.comments || data
298
+ return { content: [{ type: 'text', text: JSON.stringify(comments.map(c => ({ id: c.id, text: c.text, author: c.author?.name, createdAt: c.createdAt })), null, 2) }] }
299
+ }
300
+ )
301
+
302
+ // ── log_time ──────────────────────────────────────────────────────────────
303
+ server.tool('log_time', 'Log time entry for a project/task',
304
+ {
305
+ projectId: z.string(),
306
+ taskId: z.string().optional(),
307
+ description: z.string().optional(),
308
+ startTime: z.string().optional().describe('ISO date string for start time. Defaults to now.'),
309
+ endTime: z.string().optional().describe('ISO date string for end time. If omitted, starts a running timer.'),
310
+ workspaceId: z.string().optional(),
311
+ apiKey: z.string().optional().describe('Brioright API Key')
312
+ },
313
+ async ({ projectId, taskId, description, startTime, endTime, workspaceId, apiKey }) => {
314
+ const ws = workspaceId || DEFAULT_WORKSPACE
315
+ if (!ws) throw new Error('workspaceId is required')
316
+ const start = startTime ? new Date(startTime).toISOString() : new Date().toISOString()
317
+ const end = endTime ? new Date(endTime).toISOString() : undefined
318
+
319
+ const payload = { projectId, description, startTime: start, endTime: end }
320
+ if (taskId) payload.taskId = taskId
321
+
322
+ const data = await call('POST', `/workspaces/${ws}/time-entries`, payload, apiKey)
323
+ const entry = data.entry || data
324
+ return { content: [{ type: 'text', text: `✅ Time logged (Duration: ${entry.duration ? entry.duration + ' seconds' : 'Running timer ...'})!\n\n${JSON.stringify({ id: entry.id, description: entry.description, startTime: entry.startTime, endTime: entry.endTime, duration: entry.duration }, null, 2)}` }] }
325
+ }
326
+ )
327
+
230
328
  return server
231
329
  }
232
330
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brioright-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "MCP server for Brioright — lets AI assistants create and manage tasks via natural language",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -22,4 +22,4 @@
22
22
  "engines": {
23
23
  "node": ">=18.0.0"
24
24
  }
25
- }
25
+ }