mcp-server-moscow 0.0.2 → 0.0.4
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/README.dev.md +10 -4
- package/dist/index.js +69 -4
- package/package.json +1 -1
package/README.dev.md
CHANGED
|
@@ -15,6 +15,8 @@ The server requires the following environment variables. You can set them in a `
|
|
|
15
15
|
|
|
16
16
|
- `SUPABASE_URL`: The URL of your Supabase project.
|
|
17
17
|
- `MCP_TOKEN` (Optional): A custom token for authorization if your Edge Functions require it.
|
|
18
|
+
- `TOPIC` (Optional): Default topic for filtering tasks.
|
|
19
|
+
- `BOARD_ID` (Optional): Default board ID for filtering and assigning tasks.
|
|
18
20
|
|
|
19
21
|
## Usage
|
|
20
22
|
|
|
@@ -34,7 +36,8 @@ Add the following to your MCP configuration file (e.g., `claude_desktop_config.j
|
|
|
34
36
|
"env": {
|
|
35
37
|
"SUPABASE_URL": "https://your-project.supabase.co",
|
|
36
38
|
"MCP_TOKEN": "mcp_123.....",
|
|
37
|
-
"TOPIC": "mcp"
|
|
39
|
+
"TOPIC": "mcp",
|
|
40
|
+
"BOARD_ID": "your-board-uuid"
|
|
38
41
|
},
|
|
39
42
|
"disabled": false
|
|
40
43
|
}
|
|
@@ -75,7 +78,10 @@ npx -y moscow-mcp-server
|
|
|
75
78
|
|
|
76
79
|
## Tools Provided
|
|
77
80
|
|
|
78
|
-
- `list_tasks`: List tasks from the Moscow project. Can be filtered by `topic`.
|
|
79
|
-
- `create_task`: Create a new task.
|
|
80
|
-
- `update_task`: Update an existing task.
|
|
81
|
+
- `list_tasks`: List tasks from the Moscow project. Can be filtered by `topic` or `board_id`.
|
|
82
|
+
- `create_task`: Create a new task. Supports `board_id` to assign to a board.
|
|
83
|
+
- `update_task`: Update an existing task. Supports `board_id` to move between boards.
|
|
81
84
|
- `delete_task`: Delete a task.
|
|
85
|
+
- `find_task`: Find a task by ID or name. Supports `board_id` filtering.
|
|
86
|
+
- `list_boards`: List all boards for the current user.
|
|
87
|
+
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const __dirname = path.dirname(__filename);
|
|
|
12
12
|
dotenv.config({ path: path.resolve(__dirname, '../.env') });
|
|
13
13
|
const SUPABASE_URL = process.env.SUPABASE_URL || 'https://jnxbtfiwzaxwheifuhuf.supabase.co';
|
|
14
14
|
const DEFAULT_TOPIC = process.env.TOPIC || 'default';
|
|
15
|
+
const DEFAULT_BOARD_ID = process.env.BOARD_ID || undefined;
|
|
15
16
|
if (!SUPABASE_URL) {
|
|
16
17
|
console.error('Missing SUPABASE_URL environment variable');
|
|
17
18
|
process.exit(1);
|
|
@@ -25,12 +26,13 @@ const server = new Server({
|
|
|
25
26
|
},
|
|
26
27
|
});
|
|
27
28
|
const getEdgeFunctionUrl = () => `${SUPABASE_URL}/functions/v1/tasks`;
|
|
29
|
+
const getBoardsEdgeFunctionUrl = () => `${SUPABASE_URL}/functions/v1/boards`;
|
|
28
30
|
// Tool Handlers
|
|
29
31
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
30
32
|
tools: [
|
|
31
33
|
{
|
|
32
34
|
name: 'list_tasks',
|
|
33
|
-
description: 'List tasks for the current user and optionally filter by topic.',
|
|
35
|
+
description: 'List tasks for the current user and optionally filter by topic or board.',
|
|
34
36
|
inputSchema: {
|
|
35
37
|
type: 'object',
|
|
36
38
|
properties: {
|
|
@@ -38,6 +40,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
38
40
|
type: 'string',
|
|
39
41
|
description: 'Optional topic to filter tasks by.',
|
|
40
42
|
},
|
|
43
|
+
board_id: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Optional board ID to filter tasks by. Only tasks belonging to this board will be returned.',
|
|
46
|
+
},
|
|
41
47
|
},
|
|
42
48
|
},
|
|
43
49
|
},
|
|
@@ -69,6 +75,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
69
75
|
enum: ['TODO', 'IN_PROGRESS', 'DONE'],
|
|
70
76
|
description: 'The status of the task.',
|
|
71
77
|
},
|
|
78
|
+
board_id: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Optional board ID to assign the task to.',
|
|
81
|
+
},
|
|
72
82
|
},
|
|
73
83
|
required: ['title'],
|
|
74
84
|
},
|
|
@@ -105,6 +115,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
105
115
|
enum: ['TODO', 'IN_PROGRESS', 'DONE'],
|
|
106
116
|
description: 'The new status.',
|
|
107
117
|
},
|
|
118
|
+
board_id: {
|
|
119
|
+
type: 'string',
|
|
120
|
+
description: 'Move the task to a different board by providing the new board ID.',
|
|
121
|
+
},
|
|
108
122
|
},
|
|
109
123
|
required: ['id'],
|
|
110
124
|
},
|
|
@@ -141,9 +155,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
141
155
|
type: 'string',
|
|
142
156
|
description: 'Optional topic to narrow down the search scope.',
|
|
143
157
|
},
|
|
158
|
+
board_id: {
|
|
159
|
+
type: 'string',
|
|
160
|
+
description: 'Optional board ID to narrow down the search scope.',
|
|
161
|
+
},
|
|
144
162
|
},
|
|
145
163
|
},
|
|
146
164
|
},
|
|
165
|
+
{
|
|
166
|
+
name: 'list_boards',
|
|
167
|
+
description: 'List all boards for the current user. Useful for discovering available board IDs to filter or assign tasks.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
147
173
|
],
|
|
148
174
|
}));
|
|
149
175
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
@@ -151,12 +177,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
151
177
|
const env = extra?.env || {};
|
|
152
178
|
const mcpToken = env.MCP_TOKEN || process.env.MCP_TOKEN;
|
|
153
179
|
const topicScope = request.params.arguments?.topic || env.TOPIC || DEFAULT_TOPIC;
|
|
180
|
+
// Board from env takes priority (locks MCP to single board), otherwise use request arg or default
|
|
181
|
+
const envBoardId = env.BOARD_ID || DEFAULT_BOARD_ID;
|
|
182
|
+
const boardScope = envBoardId || request.params.arguments?.board_id;
|
|
154
183
|
const authValue = mcpToken;
|
|
155
184
|
switch (request.params.name) {
|
|
156
185
|
case 'list_tasks': {
|
|
157
186
|
try {
|
|
187
|
+
const params = {};
|
|
188
|
+
// Only filter by topic if explicitly provided (not using default)
|
|
189
|
+
const explicitTopic = request.params.arguments?.topic || env.TOPIC;
|
|
190
|
+
if (explicitTopic) {
|
|
191
|
+
params.topic = explicitTopic;
|
|
192
|
+
}
|
|
193
|
+
if (boardScope) {
|
|
194
|
+
params.board_id = boardScope;
|
|
195
|
+
}
|
|
158
196
|
const response = await axios.get(getEdgeFunctionUrl(), {
|
|
159
|
-
params
|
|
197
|
+
params,
|
|
160
198
|
headers: {
|
|
161
199
|
Authorization: `Bearer ${authValue}`,
|
|
162
200
|
},
|
|
@@ -178,7 +216,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
178
216
|
}
|
|
179
217
|
}
|
|
180
218
|
case 'create_task': {
|
|
181
|
-
const { title, description, topic, priority, status } = request.params.arguments || {};
|
|
219
|
+
const { title, description, topic, priority, status, board_id } = request.params.arguments || {};
|
|
220
|
+
// Use env board if locked, otherwise use provided board_id
|
|
221
|
+
const taskBoardId = envBoardId || board_id || null;
|
|
182
222
|
try {
|
|
183
223
|
const response = await axios.post(getEdgeFunctionUrl(), {
|
|
184
224
|
title,
|
|
@@ -186,6 +226,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
186
226
|
topic: topic || topicScope,
|
|
187
227
|
priority,
|
|
188
228
|
status,
|
|
229
|
+
board_id: taskBoardId,
|
|
189
230
|
}, {
|
|
190
231
|
headers: {
|
|
191
232
|
Authorization: `Bearer ${authValue}`,
|
|
@@ -270,7 +311,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
270
311
|
}
|
|
271
312
|
}
|
|
272
313
|
case 'find_task': {
|
|
273
|
-
const { id, name, topic } = request.params.arguments ||
|
|
314
|
+
const { id, name, topic, board_id } = request.params.arguments ||
|
|
274
315
|
{};
|
|
275
316
|
if (!id && !name) {
|
|
276
317
|
return {
|
|
@@ -287,6 +328,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
287
328
|
...(id && { id }),
|
|
288
329
|
...(name && { name }),
|
|
289
330
|
...(topic && { topic }),
|
|
331
|
+
...(board_id && { board_id }),
|
|
290
332
|
},
|
|
291
333
|
headers: {
|
|
292
334
|
Authorization: `Bearer ${authValue}`,
|
|
@@ -324,6 +366,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
324
366
|
};
|
|
325
367
|
}
|
|
326
368
|
}
|
|
369
|
+
case 'list_boards': {
|
|
370
|
+
try {
|
|
371
|
+
const response = await axios.get(getBoardsEdgeFunctionUrl(), {
|
|
372
|
+
headers: {
|
|
373
|
+
Authorization: `Bearer ${authValue}`,
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
return {
|
|
377
|
+
content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }],
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
const errorMessage = axios.isAxiosError(error)
|
|
382
|
+
? error.response?.data?.error || error.message
|
|
383
|
+
: error instanceof Error
|
|
384
|
+
? error.message
|
|
385
|
+
: String(error);
|
|
386
|
+
return {
|
|
387
|
+
content: [{ type: 'text', text: `Error listing boards: ${errorMessage}` }],
|
|
388
|
+
isError: true,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
327
392
|
default:
|
|
328
393
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
329
394
|
}
|