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.
- package/index.js +46 -38
- 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
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
{
|
|
81
|
-
|
|
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
|
)
|