brioright-mcp 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -34,27 +34,31 @@ 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
- 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'
@@ -67,9 +71,10 @@ function buildServer() {
67
71
  const server = new McpServer({ name: 'brioright', version: '1.0.0' })
68
72
 
69
73
  // ── 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')
74
+ server.tool('list_workspaces', 'List all Brioright workspaces the API key has access to',
75
+ { apiKey: z.string().optional().describe('Brioright API Key') },
76
+ async ({ apiKey }) => {
77
+ const data = await call('GET', '/workspaces', null, apiKey)
73
78
  const workspaces = data.workspaces || data
74
79
  return { content: [{ type: 'text', text: JSON.stringify(workspaces.map(w => ({ id: w.id, slug: w.slug, name: w.name })), null, 2) }] }
75
80
  }
@@ -77,11 +82,14 @@ function buildServer() {
77
82
 
78
83
  // ── list_projects ─────────────────────────────────────────────────────────
79
84
  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 }) => {
85
+ {
86
+ workspaceId: z.string().optional().describe('Workspace slug. Defaults to BRIORIGHT_WORKSPACE_ID.'),
87
+ apiKey: z.string().optional().describe('Brioright API Key')
88
+ },
89
+ async ({ workspaceId, apiKey }) => {
82
90
  const ws = workspaceId || DEFAULT_WORKSPACE
83
91
  if (!ws) throw new Error('workspaceId is required')
84
- const data = await call('GET', `/workspaces/${ws}/projects`)
92
+ const data = await call('GET', `/workspaces/${ws}/projects`, null, apiKey)
85
93
  const projects = data.projects || data
86
94
  return { content: [{ type: 'text', text: JSON.stringify(projects.map(p => ({ id: p.id, name: p.name, status: p.status })), null, 2) }] }
87
95
  }
@@ -95,15 +103,16 @@ function buildServer() {
95
103
  status: z.enum(['todo', 'in_progress', 'in_review', 'done', 'cancelled']).optional(),
96
104
  priority: z.enum(['low', 'medium', 'high', 'urgent']).optional(),
97
105
  limit: z.number().optional().default(20),
106
+ apiKey: z.string().optional().describe('Brioright API Key')
98
107
  },
99
- async ({ projectId, workspaceId, status, priority, limit }) => {
108
+ async ({ projectId, workspaceId, status, priority, limit, apiKey }) => {
100
109
  const ws = workspaceId || DEFAULT_WORKSPACE
101
110
  if (!ws) throw new Error('workspaceId is required')
102
111
  const params = new URLSearchParams()
103
112
  if (status) params.set('status', status)
104
113
  if (priority) params.set('priority', priority)
105
114
  params.set('limit', String(limit || 20))
106
- const data = await call('GET', `/workspaces/${ws}/projects/${projectId}/tasks?${params}`)
115
+ const data = await call('GET', `/workspaces/${ws}/projects/${projectId}/tasks?${params}`, null, apiKey)
107
116
  const tasks = data.tasks || data
108
117
  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
118
  }
@@ -111,11 +120,11 @@ function buildServer() {
111
120
 
112
121
  // ── get_task ──────────────────────────────────────────────────────────────
113
122
  server.tool('get_task', 'Get full details of a single task',
114
- { taskId: z.string(), workspaceId: z.string().optional() },
115
- async ({ taskId, workspaceId }) => {
123
+ { taskId: z.string(), workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
124
+ async ({ taskId, workspaceId, apiKey }) => {
116
125
  const ws = workspaceId || DEFAULT_WORKSPACE
117
126
  if (!ws) throw new Error('workspaceId is required')
118
- const data = await call('GET', `/workspaces/${ws}/tasks/${taskId}`)
127
+ const data = await call('GET', `/workspaces/${ws}/tasks/${taskId}`, null, apiKey)
119
128
  return { content: [{ type: 'text', text: JSON.stringify(data.task || data, null, 2) }] }
120
129
  }
121
130
  )
@@ -131,15 +140,16 @@ function buildServer() {
131
140
  dueDate: z.string().optional().describe('ISO date string e.g. 2026-03-15'),
132
141
  assigneeId: z.string().optional(),
133
142
  workspaceId: z.string().optional(),
143
+ apiKey: z.string().optional().describe('Brioright API Key')
134
144
  },
135
- async ({ projectId, title, description, status, priority, dueDate, assigneeId, workspaceId }) => {
145
+ async ({ projectId, title, description, status, priority, dueDate, assigneeId, workspaceId, apiKey }) => {
136
146
  const ws = workspaceId || DEFAULT_WORKSPACE
137
147
  if (!ws) throw new Error('workspaceId is required')
138
148
  const data = await call('POST', `/workspaces/${ws}/projects/${projectId}/tasks`, {
139
149
  title, description, status, priority,
140
150
  dueDate: dueDate ? new Date(dueDate).toISOString() : undefined,
141
151
  assigneeId,
142
- })
152
+ }, apiKey)
143
153
  const task = data.task || data
144
154
  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
155
  }
@@ -156,13 +166,14 @@ function buildServer() {
156
166
  dueDate: z.string().optional(),
157
167
  assigneeId: z.string().optional(),
158
168
  workspaceId: z.string().optional(),
169
+ apiKey: z.string().optional().describe('Brioright API Key')
159
170
  },
160
- async ({ taskId, workspaceId, ...fields }) => {
171
+ async ({ taskId, workspaceId, apiKey, ...fields }) => {
161
172
  const ws = workspaceId || DEFAULT_WORKSPACE
162
173
  if (!ws) throw new Error('workspaceId is required')
163
174
  const updates = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined))
164
175
  if (updates.dueDate) updates.dueDate = new Date(updates.dueDate).toISOString()
165
- const data = await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, updates)
176
+ const data = await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, updates, apiKey)
166
177
  const task = data.task || data
167
178
  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
179
  }
@@ -170,11 +181,11 @@ function buildServer() {
170
181
 
171
182
  // ── complete_task ─────────────────────────────────────────────────────────
172
183
  server.tool('complete_task', 'Mark a task as completed',
173
- { taskId: z.string(), workspaceId: z.string().optional() },
174
- async ({ taskId, workspaceId }) => {
184
+ { taskId: z.string(), workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
185
+ async ({ taskId, workspaceId, apiKey }) => {
175
186
  const ws = workspaceId || DEFAULT_WORKSPACE
176
187
  if (!ws) throw new Error('workspaceId is required')
177
- await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, { status: 'done' })
188
+ await call('PATCH', `/workspaces/${ws}/tasks/${taskId}`, { status: 'done' }, apiKey)
178
189
  return { content: [{ type: 'text', text: `✅ Task ${taskId} marked as done.` }] }
179
190
  }
180
191
  )
@@ -186,11 +197,12 @@ function buildServer() {
186
197
  description: z.string().optional(),
187
198
  color: z.string().optional().default('#6366f1'),
188
199
  workspaceId: z.string().optional(),
200
+ apiKey: z.string().optional().describe('Brioright API Key')
189
201
  },
190
- async ({ name, description, color, workspaceId }) => {
202
+ async ({ name, description, color, workspaceId, apiKey }) => {
191
203
  const ws = workspaceId || DEFAULT_WORKSPACE
192
204
  if (!ws) throw new Error('workspaceId is required')
193
- const data = await call('POST', `/workspaces/${ws}/projects`, { name, description, color })
205
+ const data = await call('POST', `/workspaces/${ws}/projects`, { name, description, color }, apiKey)
194
206
  const project = data.project || data
195
207
  return { content: [{ type: 'text', text: `✅ Project created!\n\n${JSON.stringify({ id: project.id, name: project.name }, null, 2)}` }] }
196
208
  }
@@ -198,11 +210,11 @@ function buildServer() {
198
210
 
199
211
  // ── list_members ──────────────────────────────────────────────────────────
200
212
  server.tool('list_members', 'List workspace members (useful for finding assignee IDs)',
201
- { workspaceId: z.string().optional() },
202
- async ({ workspaceId }) => {
213
+ { workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
214
+ async ({ workspaceId, apiKey }) => {
203
215
  const ws = workspaceId || DEFAULT_WORKSPACE
204
216
  if (!ws) throw new Error('workspaceId is required')
205
- const data = await call('GET', `/workspaces/${ws}/members`)
217
+ const data = await call('GET', `/workspaces/${ws}/members`, null, apiKey)
206
218
  const members = data.members || data
207
219
  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
220
  }
@@ -210,11 +222,11 @@ function buildServer() {
210
222
 
211
223
  // ── get_workspace_summary ─────────────────────────────────────────────────
212
224
  server.tool('get_workspace_summary', 'Dashboard stats: task counts by status and priority',
213
- { workspaceId: z.string().optional() },
214
- async ({ workspaceId }) => {
225
+ { workspaceId: z.string().optional(), apiKey: z.string().optional().describe('Brioright API Key') },
226
+ async ({ workspaceId, apiKey }) => {
215
227
  const ws = workspaceId || DEFAULT_WORKSPACE
216
228
  if (!ws) throw new Error('workspaceId is required')
217
- const data = await call('GET', `/workspaces/${ws}/dashboard/stats`)
229
+ const data = await call('GET', `/workspaces/${ws}/dashboard/stats`, null, apiKey)
218
230
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
219
231
  }
220
232
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brioright-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
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
+ }
package/test-axios.mjs ADDED
@@ -0,0 +1,20 @@
1
+ import axios from 'axios';
2
+
3
+ async function test() {
4
+ const api = axios.create({
5
+ baseURL: 'https://brioright.online/api',
6
+ headers: { 'X-API-Key': 'brio_9847f9c385632d4d1c888afc42c8c3906ffad22bf7713c062a2d1c6db90adadf', 'Content-Type': 'application/json' },
7
+ timeout: 10000,
8
+ });
9
+
10
+ try {
11
+ const res = await api({ method: 'GET', url: '/workspaces', data: null });
12
+ console.log("SUCCESS:");
13
+ console.log(res.data);
14
+ } catch (err) {
15
+ console.log("ERROR:");
16
+ console.log(err.response?.data || err.message);
17
+ }
18
+ }
19
+
20
+ test();
@@ -0,0 +1,20 @@
1
+ import axios from 'axios';
2
+
3
+ async function test() {
4
+ const api = axios.create({
5
+ baseURL: 'https://brioright.online/api',
6
+ headers: { 'X-API-Key': 'brio_9847f9c385632d4d1c888afc42c8c3906ffad22bf7713c062a2d1c6db90adadf', 'Content-Type': 'application/json' },
7
+ timeout: 10000,
8
+ });
9
+
10
+ try {
11
+ const res = await api({ method: 'GET', url: '/workspaces' }); // data: undefined
12
+ console.log("SUCCESS:");
13
+ console.log(res.data);
14
+ } catch (err) {
15
+ console.log("ERROR:");
16
+ console.log(err.response?.data || err.message);
17
+ }
18
+ }
19
+
20
+ test();