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 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: { topic: topicScope },
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-moscow",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "description": "MCP for Moscow task management",
6
6
  "main": "dist/index.js",