brioright-mcp 1.1.0 → 1.2.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 +46 -38
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -34,25 +34,25 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
34
34
  config({ path: join(__dirname, '.env') })
35
35
 
36
36
  const API_URL = process.env.BRIORIGHT_API_URL || 'http://localhost:3001/api'
37
- const API_KEY = process.env.BRIORIGHT_API_KEY
37
+ const ENV_API_KEY = process.env.BRIORIGHT_API_KEY
38
38
  const DEFAULT_WORKSPACE = process.env.BRIORIGHT_WORKSPACE_ID
39
39
  const MCP_PORT = parseInt(process.env.MCP_PORT || '4040')
40
40
  const MCP_SECRET = process.env.MCP_SECRET // Optional bearer token for HTTP mode
41
41
  const USE_HTTP = process.env.MCP_TRANSPORT === 'http'
42
42
 
43
- if (!API_KEY) {
44
- process.stderr.write('[Brioright MCP] ERROR: BRIORIGHT_API_KEY is not set.\n')
45
- process.exit(1)
46
- }
43
+ // ── Axios client factory ──────────────────────────────────────────────────────
44
+ async function call(method, path, data, overrideApiKey) {
45
+ const key = overrideApiKey || ENV_API_KEY;
46
+ if (!key) {
47
+ throw new Error('Brioright API error: No API Key provided. Either set BRIORIGHT_API_KEY environment variable or provide apiKey in the tool arguments.');
48
+ }
47
49
 
48
- // ── Axios client ──────────────────────────────────────────────────────────────
49
- const api = axios.create({
50
- baseURL: API_URL,
51
- headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
52
- timeout: 10000,
53
- })
50
+ const api = axios.create({
51
+ baseURL: API_URL,
52
+ headers: { 'X-API-Key': key, 'Content-Type': 'application/json' },
53
+ timeout: 10000,
54
+ })
54
55
 
55
- async function call(method, path, data) {
56
56
  try {
57
57
  const res = await api({ method, url: path, data })
58
58
  return res.data.data
@@ -67,9 +67,10 @@ function buildServer() {
67
67
  const server = new McpServer({ name: 'brioright', version: '1.0.0' })
68
68
 
69
69
  // ── list_workspaces ───────────────────────────────────────────────────────
70
- server.tool('list_workspaces', 'List all Brioright workspaces the API key has access to', {},
71
- async () => {
72
- const data = await call('GET', '/workspaces')
70
+ server.tool('list_workspaces', 'List all Brioright workspaces the API key has access to',
71
+ { apiKey: z.string().optional().describe('Brioright API Key') },
72
+ async ({ apiKey }) => {
73
+ const data = await call('GET', '/workspaces', null, apiKey)
73
74
  const workspaces = data.workspaces || data
74
75
  return { content: [{ type: 'text', text: JSON.stringify(workspaces.map(w => ({ id: w.id, slug: w.slug, name: w.name })), null, 2) }] }
75
76
  }
@@ -77,11 +78,14 @@ function buildServer() {
77
78
 
78
79
  // ── list_projects ─────────────────────────────────────────────────────────
79
80
  server.tool('list_projects', 'List all projects in a workspace',
80
- { workspaceId: z.string().optional().describe('Workspace slug. Defaults to BRIORIGHT_WORKSPACE_ID.') },
81
- async ({ workspaceId }) => {
81
+ {
82
+ workspaceId: z.string().optional().describe('Workspace slug. Defaults to BRIORIGHT_WORKSPACE_ID.'),
83
+ apiKey: z.string().optional().describe('Brioright API Key')
84
+ },
85
+ async ({ workspaceId, apiKey }) => {
82
86
  const ws = workspaceId || DEFAULT_WORKSPACE
83
87
  if (!ws) throw new Error('workspaceId is required')
84
- const data = await call('GET', `/workspaces/${ws}/projects`)
88
+ const data = await call('GET', `/workspaces/${ws}/projects`, null, apiKey)
85
89
  const projects = data.projects || data
86
90
  return { content: [{ type: 'text', text: JSON.stringify(projects.map(p => ({ id: p.id, name: p.name, status: p.status })), null, 2) }] }
87
91
  }
@@ -95,15 +99,16 @@ function buildServer() {
95
99
  status: z.enum(['todo', 'in_progress', 'in_review', 'done', 'cancelled']).optional(),
96
100
  priority: z.enum(['low', 'medium', 'high', 'urgent']).optional(),
97
101
  limit: z.number().optional().default(20),
102
+ apiKey: z.string().optional().describe('Brioright API Key')
98
103
  },
99
- async ({ projectId, workspaceId, status, priority, limit }) => {
104
+ async ({ projectId, workspaceId, status, priority, limit, apiKey }) => {
100
105
  const ws = workspaceId || DEFAULT_WORKSPACE
101
106
  if (!ws) throw new Error('workspaceId is required')
102
107
  const params = new URLSearchParams()
103
108
  if (status) params.set('status', status)
104
109
  if (priority) params.set('priority', priority)
105
110
  params.set('limit', String(limit || 20))
106
- const data = await call('GET', `/workspaces/${ws}/projects/${projectId}/tasks?${params}`)
111
+ const data = await call('GET', `/workspaces/${ws}/projects/${projectId}/tasks?${params}`, null, apiKey)
107
112
  const tasks = data.tasks || data
108
113
  return { content: [{ type: 'text', text: JSON.stringify(tasks.map(t => ({ id: t.id, title: t.title, status: t.status, priority: t.priority, dueDate: t.dueDate, assignee: t.assignee?.name })), null, 2) }] }
109
114
  }
@@ -111,11 +116,11 @@ function buildServer() {
111
116
 
112
117
  // ── get_task ──────────────────────────────────────────────────────────────
113
118
  server.tool('get_task', 'Get full details of a single task',
114
- { taskId: z.string(), workspaceId: z.string().optional() },
115
- async ({ taskId, workspaceId }) => {
119
+ { taskId: z.string(), workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
120
+ async ({ taskId, workspaceId, apiKey }) => {
116
121
  const ws = workspaceId || DEFAULT_WORKSPACE
117
122
  if (!ws) throw new Error('workspaceId is required')
118
- const data = await call('GET', `/workspaces/${ws}/tasks/${taskId}`)
123
+ const data = await call('GET', `/workspaces/${ws}/tasks/${taskId}`, null, apiKey)
119
124
  return { content: [{ type: 'text', text: JSON.stringify(data.task || data, null, 2) }] }
120
125
  }
121
126
  )
@@ -131,15 +136,16 @@ function buildServer() {
131
136
  dueDate: z.string().optional().describe('ISO date string e.g. 2026-03-15'),
132
137
  assigneeId: z.string().optional(),
133
138
  workspaceId: z.string().optional(),
139
+ apiKey: z.string().optional().describe('Brioright API Key')
134
140
  },
135
- async ({ projectId, title, description, status, priority, dueDate, assigneeId, workspaceId }) => {
141
+ async ({ projectId, title, description, status, priority, dueDate, assigneeId, workspaceId, apiKey }) => {
136
142
  const ws = workspaceId || DEFAULT_WORKSPACE
137
143
  if (!ws) throw new Error('workspaceId is required')
138
144
  const data = await call('POST', `/workspaces/${ws}/projects/${projectId}/tasks`, {
139
145
  title, description, status, priority,
140
146
  dueDate: dueDate ? new Date(dueDate).toISOString() : undefined,
141
147
  assigneeId,
142
- })
148
+ }, apiKey)
143
149
  const task = data.task || data
144
150
  return { content: [{ type: 'text', text: `✅ Task created!\n\n${JSON.stringify({ id: task.id, title: task.title, status: task.status, priority: task.priority, dueDate: task.dueDate }, null, 2)}` }] }
145
151
  }
@@ -156,13 +162,14 @@ function buildServer() {
156
162
  dueDate: z.string().optional(),
157
163
  assigneeId: z.string().optional(),
158
164
  workspaceId: z.string().optional(),
165
+ apiKey: z.string().optional().describe('Brioright API Key')
159
166
  },
160
- async ({ taskId, workspaceId, ...fields }) => {
167
+ async ({ taskId, workspaceId, apiKey, ...fields }) => {
161
168
  const ws = workspaceId || DEFAULT_WORKSPACE
162
169
  if (!ws) throw new Error('workspaceId is required')
163
170
  const updates = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined))
164
171
  if (updates.dueDate) updates.dueDate = new Date(updates.dueDate).toISOString()
165
- const data = await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, updates)
172
+ const data = await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, updates, apiKey)
166
173
  const task = data.task || data
167
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)}` }] }
168
175
  }
@@ -170,11 +177,11 @@ function buildServer() {
170
177
 
171
178
  // ── complete_task ─────────────────────────────────────────────────────────
172
179
  server.tool('complete_task', 'Mark a task as completed',
173
- { taskId: z.string(), workspaceId: z.string().optional() },
174
- async ({ taskId, workspaceId }) => {
180
+ { taskId: z.string(), workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
181
+ async ({ taskId, workspaceId, apiKey }) => {
175
182
  const ws = workspaceId || DEFAULT_WORKSPACE
176
183
  if (!ws) throw new Error('workspaceId is required')
177
- await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, { status: 'done' })
184
+ await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, { status: 'done' }, apiKey)
178
185
  return { content: [{ type: 'text', text: `✅ Task ${taskId} marked as done.` }] }
179
186
  }
180
187
  )
@@ -186,11 +193,12 @@ function buildServer() {
186
193
  description: z.string().optional(),
187
194
  color: z.string().optional().default('#6366f1'),
188
195
  workspaceId: z.string().optional(),
196
+ apiKey: z.string().optional().describe('Brioright API Key')
189
197
  },
190
- async ({ name, description, color, workspaceId }) => {
198
+ async ({ name, description, color, workspaceId, apiKey }) => {
191
199
  const ws = workspaceId || DEFAULT_WORKSPACE
192
200
  if (!ws) throw new Error('workspaceId is required')
193
- const data = await call('POST', `/workspaces/${ws}/projects`, { name, description, color })
201
+ const data = await call('POST', `/workspaces/${ws}/projects`, { name, description, color }, apiKey)
194
202
  const project = data.project || data
195
203
  return { content: [{ type: 'text', text: `✅ Project created!\n\n${JSON.stringify({ id: project.id, name: project.name }, null, 2)}` }] }
196
204
  }
@@ -198,11 +206,11 @@ function buildServer() {
198
206
 
199
207
  // ── list_members ──────────────────────────────────────────────────────────
200
208
  server.tool('list_members', 'List workspace members (useful for finding assignee IDs)',
201
- { workspaceId: z.string().optional() },
202
- async ({ workspaceId }) => {
209
+ { workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
210
+ async ({ workspaceId, apiKey }) => {
203
211
  const ws = workspaceId || DEFAULT_WORKSPACE
204
212
  if (!ws) throw new Error('workspaceId is required')
205
- const data = await call('GET', `/workspaces/${ws}/members`)
213
+ const data = await call('GET', `/workspaces/${ws}/members`, null, apiKey)
206
214
  const members = data.members || data
207
215
  return { content: [{ type: 'text', text: JSON.stringify(members.map(m => ({ id: m.user?.id || m.id, name: m.user?.name || m.name, email: m.user?.email || m.email, role: m.role })), null, 2) }] }
208
216
  }
@@ -210,11 +218,11 @@ function buildServer() {
210
218
 
211
219
  // ── get_workspace_summary ─────────────────────────────────────────────────
212
220
  server.tool('get_workspace_summary', 'Dashboard stats: task counts by status and priority',
213
- { workspaceId: z.string().optional() },
214
- async ({ workspaceId }) => {
221
+ { workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
222
+ async ({ workspaceId, apiKey }) => {
215
223
  const ws = workspaceId || DEFAULT_WORKSPACE
216
224
  if (!ws) throw new Error('workspaceId is required')
217
- const data = await call('GET', `/workspaces/${ws}/dashboard/stats`)
225
+ const data = await call('GET', `/workspaces/${ws}/dashboard/stats`, null, apiKey)
218
226
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
219
227
  }
220
228
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brioright-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.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",