mcp-server-moscow 0.0.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/README.dev.md ADDED
@@ -0,0 +1,81 @@
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
+ - `MCP_TOKEN` (Optional): A custom token for authorization if your Edge Functions require it.
18
+
19
+ ## Usage
20
+
21
+ ### Using with Claude Desktop or Antigravity
22
+
23
+ Add the following to your MCP configuration file (e.g., `claude_desktop_config.json` or `mcp_config.json`):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "moscow": {
29
+ "command": "node",
30
+ "args": [
31
+ "/Users/guiller/WebstormProjects/moscow-task/mcp-server/dist/index.js",
32
+ "--stdio"
33
+ ],
34
+ "env": {
35
+ "SUPABASE_URL": "https://your-project.supabase.co",
36
+ "MCP_TOKEN": "mcp_123.....",
37
+ "TOPIC": "mcp"
38
+ },
39
+ "disabled": false
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ### Running Locally (Development)
46
+
47
+ 1. **Install dependencies**:
48
+ ```bash
49
+ npm install
50
+ ```
51
+
52
+ 2. **Build**:
53
+ ```bash
54
+ npm run build
55
+ ```
56
+
57
+ ### Deploy to NPM
58
+
59
+ ```markdown
60
+ 1. **Login to NPM**:
61
+ ```bash
62
+ npm login
63
+ ```
64
+
65
+ 2. **Publish**:
66
+ ```bash
67
+ npm publish --access public
68
+ ```
69
+
70
+ After publishing, anyone can use your server with:
71
+ ```bash
72
+ npx -y moscow-mcp-server
73
+ ```
74
+ ```
75
+
76
+ ## Tools Provided
77
+
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
+ - `delete_task`: Delete a task.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Moscow MCP Server
2
+
3
+ This is a **Model Context Protocol (MCP)** server for the **Moscow Task Management** system. It allows AI assistants like Claude Desktop or Antigravity to interact with your personal task list directly.
4
+
5
+ ## What is this for?
6
+
7
+ By connecting this MCP server to your AI assistant, you can:
8
+ - **List your tasks**: Ask "What tasks do I have?"
9
+ - **Create tasks**: Say "Add a task to check email in the morning."
10
+ - **Update tasks**: "Mark the email task as done."
11
+ - **Manage priorities**: "Review my MUST priority tasks."
12
+
13
+ ## Prerequisites
14
+
15
+ 1. **Sign Up**: You must have an account on the Moscow Task Manager app at [https://moscow-task.pages.dev/](https://moscow-task.pages.dev/).
16
+
17
+ ## How to Get Your Token
18
+
19
+ To secure your task data, this MCP server requires a personal access token.
20
+
21
+ 1. **Log in** to [https://moscow-task.pages.dev/](https://moscow-task.pages.dev/).
22
+ 2. Open the **Settings** menu (click the gear icon or your profile).
23
+ 3. Scroll down to the **Moscow MCP Tokens** section.
24
+ 4. Enter a name for your token (e.g., "Claude Desktop") in the input field.
25
+ 5. Click the **+ (Plus)** button to generate the token.
26
+ 6. Click the **Copy** icon next to your new token to copy it to your clipboard.
27
+
28
+ > **Keep this token secret!** It gives full access to your tasks.
29
+
30
+ ## Configuration
31
+
32
+ ### 1. Using with Claude Desktop
33
+
34
+ Add the following to your Claude Desktop configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "moscow": {
40
+ "command": "npx",
41
+ "args": [
42
+ "-y",
43
+ "mcp-server-moscow"
44
+ ],
45
+ "env": {
46
+ "MCP_TOKEN": "paste-your-token-here"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ *Replace `paste-your-token-here` with the token you copied from the website.*
54
+
55
+ ### 2. Manual / Local Development
56
+
57
+ If you are running the server from source:
58
+
59
+ 1. Clone the repository.
60
+ 2. Install dependencies: `npm install`
61
+ 3. Build the project: `npm run build`
62
+ 4. Add to your MCP config using the full path to the built file:
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "moscow-local": {
68
+ "command": "node",
69
+ "args": [
70
+ "/absolute/path/to/moscow-task/mcp-server/dist/index.js"
71
+ ],
72
+ "env": {
73
+ "MCP_TOKEN": "paste-your-token-here"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
+ import axios from 'axios';
6
+ import dotenv from 'dotenv';
7
+ import { fileURLToPath } from 'url';
8
+ import path from 'path';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ // Load .env from the project root relative to this file's location
12
+ dotenv.config({ path: path.resolve(__dirname, '../.env') });
13
+ const SUPABASE_URL = process.env.SUPABASE_URL || 'https://jnxbtfiwzaxwheifuhuf.supabase.co';
14
+ const DEFAULT_TOPIC = process.env.TOPIC || 'default';
15
+ if (!SUPABASE_URL) {
16
+ console.error('Missing SUPABASE_URL environment variable');
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;
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
+ },
142
+ });
143
+ return {
144
+ content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }],
145
+ };
146
+ }
147
+ catch (error) {
148
+ const errorMessage = axios.isAxiosError(error)
149
+ ? error.response?.data?.error || error.message
150
+ : error instanceof Error
151
+ ? error.message
152
+ : String(error);
153
+ return {
154
+ content: [{ type: 'text', text: `Error listing tasks: ${errorMessage}` }],
155
+ isError: true,
156
+ };
157
+ }
158
+ }
159
+ case 'create_task': {
160
+ const { title, description, topic, priority, status } = request.params.arguments || {};
161
+ try {
162
+ const response = await axios.post(getEdgeFunctionUrl(), {
163
+ title,
164
+ description,
165
+ topic: topic || topicScope,
166
+ priority,
167
+ status,
168
+ }, {
169
+ headers: {
170
+ Authorization: `Bearer ${authValue}`,
171
+ },
172
+ });
173
+ return {
174
+ content: [
175
+ {
176
+ type: 'text',
177
+ text: `Task created successfully: ${JSON.stringify(response.data, null, 2)}`,
178
+ },
179
+ ],
180
+ };
181
+ }
182
+ catch (error) {
183
+ const errorMessage = axios.isAxiosError(error)
184
+ ? error.response?.data?.error || error.message
185
+ : error instanceof Error
186
+ ? error.message
187
+ : String(error);
188
+ return {
189
+ content: [{ type: 'text', text: `Error creating task: ${errorMessage}` }],
190
+ isError: true,
191
+ };
192
+ }
193
+ }
194
+ case 'update_task': {
195
+ const { id, ...updates } = request.params.arguments || {};
196
+ try {
197
+ const response = await axios.patch(getEdgeFunctionUrl(), {
198
+ id,
199
+ ...updates,
200
+ }, {
201
+ headers: {
202
+ Authorization: `Bearer ${authValue}`,
203
+ },
204
+ });
205
+ return {
206
+ content: [
207
+ {
208
+ type: 'text',
209
+ text: `Task updated successfully: ${JSON.stringify(response.data, null, 2)}`,
210
+ },
211
+ ],
212
+ };
213
+ }
214
+ catch (error) {
215
+ const errorMessage = axios.isAxiosError(error)
216
+ ? error.response?.data?.error || error.message
217
+ : error instanceof Error
218
+ ? error.message
219
+ : String(error);
220
+ return {
221
+ content: [{ type: 'text', text: `Error updating task: ${errorMessage}` }],
222
+ isError: true,
223
+ };
224
+ }
225
+ }
226
+ case 'delete_task': {
227
+ const { id } = request.params.arguments || {};
228
+ try {
229
+ await axios.delete(getEdgeFunctionUrl(), {
230
+ params: { id },
231
+ headers: {
232
+ Authorization: `Bearer ${authValue}`,
233
+ },
234
+ });
235
+ return {
236
+ content: [{ type: 'text', text: `Task ${id} deleted successfully.` }],
237
+ };
238
+ }
239
+ catch (error) {
240
+ const errorMessage = axios.isAxiosError(error)
241
+ ? error.response?.data?.error || error.message
242
+ : error instanceof Error
243
+ ? error.message
244
+ : String(error);
245
+ return {
246
+ content: [{ type: 'text', text: `Error deleting task: ${errorMessage}` }],
247
+ isError: true,
248
+ };
249
+ }
250
+ }
251
+ default:
252
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
253
+ }
254
+ });
255
+ // STARTUP LOGIC: Always use Stdio
256
+ async function main() {
257
+ const transport = new StdioServerTransport();
258
+ await server.connect(transport);
259
+ console.error('Moscow MCP Server running on Stdio');
260
+ }
261
+ main().catch((error) => {
262
+ console.error('Server error:', error);
263
+ process.exit(1);
264
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "mcp-server-moscow",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "MCP 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
+ }