moscow-mcp-server 1.0.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 (3) hide show
  1. package/README.md +64 -0
  2. package/dist/index.js +268 -0
  3. package/package.json +26 -0
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Moscow MCP Server (Local Adapter)
2
+
3
+ A Model Context Protocol (MCP) server that provides tools to interact with the Moscow task management system via Supabase Edge Functions.
4
+
5
+ This server is designed to run locally (as a "Local Adapter") and communicate with your remote Supabase instance via standard HTTP requests.
6
+
7
+ ## Prerequisites
8
+
9
+ - Node.js (v18 or higher)
10
+ - A Supabase project with the Moscow task management Edge Functions deployed.
11
+
12
+ ## Configuration
13
+
14
+ The server requires the following environment variables. You can set them in a `.env` file in the root directory or pass them via your MCP client configuration.
15
+
16
+ - `SUPABASE_URL`: The URL of your Supabase project.
17
+ - `SUPABASE_ANON_KEY`: The anonymous API key for your Supabase project.
18
+ - `MCP_TOKEN` (Optional): A custom token for authorization if your Edge Functions require it.
19
+
20
+ ## Usage
21
+
22
+ ### Using with Claude Desktop or Antigravity
23
+
24
+ Add the following to your MCP configuration file (e.g., `claude_desktop_config.json` or `mcp_config.json`):
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "moscow": {
30
+ "command": "node",
31
+ "args": [
32
+ "/Users/guiller/WebstormProjects/moscow-task/mcp-server/dist/index.js",
33
+ "--stdio"
34
+ ],
35
+ "env": {
36
+ "SUPABASE_URL": "https://your-project.supabase.co",
37
+ "SUPABASE_ANON_KEY": "your-anon-key",
38
+ "MCP_TOKEN": "mcp_123.....",
39
+ "TOPIC": "mcp"
40
+ },
41
+ "disabled": false
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Running Locally (Development)
48
+
49
+ 1. **Install dependencies**:
50
+ ```bash
51
+ npm install
52
+ ```
53
+
54
+ 2. **Build**:
55
+ ```bash
56
+ npm run build
57
+ ```
58
+
59
+ ## Tools Provided
60
+
61
+ - `list_tasks`: List tasks from the Moscow project. Can be filtered by `topic`.
62
+ - `create_task`: Create a new task.
63
+ - `update_task`: Update an existing task.
64
+ - `delete_task`: Delete a task.
package/dist/index.js ADDED
@@ -0,0 +1,268 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
4
+ import axios from 'axios';
5
+ import dotenv from 'dotenv';
6
+ import { fileURLToPath } from 'url';
7
+ import path from 'path';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ // Load .env from the project root relative to this file's location
11
+ dotenv.config({ path: path.resolve(__dirname, '../.env') });
12
+ const SUPABASE_URL = process.env.SUPABASE_URL;
13
+ const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;
14
+ const DEFAULT_TOPIC = process.env.TOPIC || 'default';
15
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
16
+ console.error('Missing SUPABASE_URL or SUPABASE_ANON_KEY environment variables');
17
+ process.exit(1);
18
+ }
19
+ const server = new Server({
20
+ name: 'moscow-mcp-server',
21
+ version: '1.0.0',
22
+ }, {
23
+ capabilities: {
24
+ tools: {},
25
+ },
26
+ });
27
+ const getEdgeFunctionUrl = () => `${SUPABASE_URL}/functions/v1/tasks`;
28
+ // Tool Handlers
29
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
30
+ tools: [
31
+ {
32
+ name: 'list_tasks',
33
+ description: 'List tasks for the current user and optionally filter by topic.',
34
+ inputSchema: {
35
+ type: 'object',
36
+ properties: {
37
+ topic: {
38
+ type: 'string',
39
+ description: 'Optional topic to filter tasks by.',
40
+ },
41
+ },
42
+ },
43
+ },
44
+ {
45
+ name: 'create_task',
46
+ description: 'Create a new task.',
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {
50
+ title: {
51
+ type: 'string',
52
+ description: 'The title of the task.',
53
+ },
54
+ description: {
55
+ type: 'string',
56
+ description: 'A detailed description of the task.',
57
+ },
58
+ topic: {
59
+ type: 'string',
60
+ description: 'The topic/category of the task.',
61
+ },
62
+ priority: {
63
+ type: 'string',
64
+ enum: ['MUST', 'SHOULD', 'COULD', 'WONT'],
65
+ description: 'The priority of the task.',
66
+ },
67
+ status: {
68
+ type: 'string',
69
+ enum: ['TODO', 'IN_PROGRESS', 'DONE'],
70
+ description: 'The status of the task.',
71
+ },
72
+ },
73
+ required: ['title'],
74
+ },
75
+ },
76
+ {
77
+ name: 'update_task',
78
+ description: 'Update an existing task.',
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ id: {
83
+ type: 'string',
84
+ description: 'The ID of the task to update.',
85
+ },
86
+ title: {
87
+ type: 'string',
88
+ description: 'The new title of the task.',
89
+ },
90
+ description: {
91
+ type: 'string',
92
+ description: 'The new description of the task.',
93
+ },
94
+ topic: {
95
+ type: 'string',
96
+ description: 'The new topic/category of the task.',
97
+ },
98
+ priority: {
99
+ type: 'string',
100
+ enum: ['MUST', 'SHOULD', 'COULD', 'WONT'],
101
+ description: 'The new priority.',
102
+ },
103
+ status: {
104
+ type: 'string',
105
+ enum: ['TODO', 'IN_PROGRESS', 'DONE'],
106
+ description: 'The new status.',
107
+ },
108
+ },
109
+ required: ['id'],
110
+ },
111
+ },
112
+ {
113
+ name: 'delete_task',
114
+ description: 'Delete a task.',
115
+ inputSchema: {
116
+ type: 'object',
117
+ properties: {
118
+ id: {
119
+ type: 'string',
120
+ description: 'The ID of the task to delete.',
121
+ },
122
+ },
123
+ required: ['id'],
124
+ },
125
+ },
126
+ ],
127
+ }));
128
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
129
+ // Extract dynamic auth from the transport metadata if available
130
+ const env = extra?.env || {};
131
+ const mcpToken = env.MCP_TOKEN || process.env.MCP_TOKEN;
132
+ const topicScope = request.params.arguments?.topic || env.TOPIC || DEFAULT_TOPIC;
133
+ const authValue = mcpToken || SUPABASE_ANON_KEY;
134
+ switch (request.params.name) {
135
+ case 'list_tasks': {
136
+ try {
137
+ const response = await axios.get(getEdgeFunctionUrl(), {
138
+ params: { topic: topicScope },
139
+ headers: {
140
+ Authorization: `Bearer ${authValue}`,
141
+ apikey: SUPABASE_ANON_KEY,
142
+ },
143
+ });
144
+ return {
145
+ content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }],
146
+ };
147
+ }
148
+ catch (error) {
149
+ const errorMessage = axios.isAxiosError(error)
150
+ ? error.response?.data?.error || error.message
151
+ : error instanceof Error
152
+ ? error.message
153
+ : String(error);
154
+ return {
155
+ content: [{ type: 'text', text: `Error listing tasks: ${errorMessage}` }],
156
+ isError: true,
157
+ };
158
+ }
159
+ }
160
+ case 'create_task': {
161
+ const { title, description, topic, priority, status } = request.params.arguments || {};
162
+ try {
163
+ const response = await axios.post(getEdgeFunctionUrl(), {
164
+ title,
165
+ description,
166
+ topic: topic || topicScope,
167
+ priority,
168
+ status,
169
+ }, {
170
+ headers: {
171
+ Authorization: `Bearer ${authValue}`,
172
+ apikey: SUPABASE_ANON_KEY,
173
+ },
174
+ });
175
+ return {
176
+ content: [
177
+ {
178
+ type: 'text',
179
+ text: `Task created successfully: ${JSON.stringify(response.data, null, 2)}`,
180
+ },
181
+ ],
182
+ };
183
+ }
184
+ catch (error) {
185
+ const errorMessage = axios.isAxiosError(error)
186
+ ? error.response?.data?.error || error.message
187
+ : error instanceof Error
188
+ ? error.message
189
+ : String(error);
190
+ return {
191
+ content: [{ type: 'text', text: `Error creating task: ${errorMessage}` }],
192
+ isError: true,
193
+ };
194
+ }
195
+ }
196
+ case 'update_task': {
197
+ const { id, ...updates } = request.params.arguments || {};
198
+ try {
199
+ const response = await axios.patch(getEdgeFunctionUrl(), {
200
+ id,
201
+ ...updates,
202
+ }, {
203
+ headers: {
204
+ Authorization: `Bearer ${authValue}`,
205
+ apikey: SUPABASE_ANON_KEY,
206
+ },
207
+ });
208
+ return {
209
+ content: [
210
+ {
211
+ type: 'text',
212
+ text: `Task updated successfully: ${JSON.stringify(response.data, null, 2)}`,
213
+ },
214
+ ],
215
+ };
216
+ }
217
+ catch (error) {
218
+ const errorMessage = axios.isAxiosError(error)
219
+ ? error.response?.data?.error || error.message
220
+ : error instanceof Error
221
+ ? error.message
222
+ : String(error);
223
+ return {
224
+ content: [{ type: 'text', text: `Error updating task: ${errorMessage}` }],
225
+ isError: true,
226
+ };
227
+ }
228
+ }
229
+ case 'delete_task': {
230
+ const { id } = request.params.arguments || {};
231
+ try {
232
+ await axios.delete(getEdgeFunctionUrl(), {
233
+ params: { id },
234
+ headers: {
235
+ Authorization: `Bearer ${authValue}`,
236
+ apikey: SUPABASE_ANON_KEY,
237
+ },
238
+ });
239
+ return {
240
+ content: [{ type: 'text', text: `Task ${id} deleted successfully.` }],
241
+ };
242
+ }
243
+ catch (error) {
244
+ const errorMessage = axios.isAxiosError(error)
245
+ ? error.response?.data?.error || error.message
246
+ : error instanceof Error
247
+ ? error.message
248
+ : String(error);
249
+ return {
250
+ content: [{ type: 'text', text: `Error deleting task: ${errorMessage}` }],
251
+ isError: true,
252
+ };
253
+ }
254
+ }
255
+ default:
256
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
257
+ }
258
+ });
259
+ // STARTUP LOGIC: Always use Stdio
260
+ async function main() {
261
+ const transport = new StdioServerTransport();
262
+ await server.connect(transport);
263
+ console.error('Moscow MCP Server running on Stdio');
264
+ }
265
+ main().catch((error) => {
266
+ console.error('Server error:', error);
267
+ process.exit(1);
268
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "moscow-mcp-server",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "MCP server for Moscow task management",
6
+ "main": "dist/index.js",
7
+ "bin": "dist/index.js",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "start": "node dist/index.js",
14
+ "dev": "ts-node src/index.ts"
15
+ },
16
+ "dependencies": {
17
+ "@modelcontextprotocol/sdk": "^0.6.0",
18
+ "axios": "^1.6.0",
19
+ "dotenv": "^16.3.1"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.8.0",
23
+ "ts-node": "^10.9.1",
24
+ "typescript": "^5.2.2"
25
+ }
26
+ }