@lil2good/nubis-mcp-server 1.0.8

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.md ADDED
@@ -0,0 +1,87 @@
1
+ # Nubis MCP Server
2
+
3
+ ## What is MCP?
4
+
5
+ The Model Context Protocol (MCP) is a standardized interface that allows AI models to access external tools and data sources. This server implements the MCP specification to provide AI assistants with access to Nubis task management functionality.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npx -y @lil2good/nubis-mcp-server@latest --workspaceID <your-workspace-id> --access-token <your-api-key>
11
+ ```
12
+
13
+ ## Available Tools
14
+
15
+ This MCP server provides the following tools:
16
+
17
+ ### `get_tasks`
18
+ Retrieves a list of tasks for a workspace with optional filtering by board.
19
+
20
+ ```
21
+ Parameters:
22
+ - limit: number (optional, default: 5)
23
+ - board: 'bugs' | 'backlog' | 'priority' | 'in-progress' | 'reviewing' | 'completed' (optional)
24
+ ```
25
+
26
+ ### `get_task`
27
+ Gets detailed information about a specific task by ID.
28
+
29
+ ```
30
+ Parameters:
31
+ - taskID: string (required)
32
+ ```
33
+
34
+ ### `get_task_images`
35
+ Retrieves images associated with a specific task.
36
+
37
+ ```
38
+ Parameters:
39
+ - taskID: string (required)
40
+ ```
41
+
42
+ ### `work_on_task`
43
+ Moves a task to the "in-progress" board and returns task details.
44
+
45
+ ```
46
+ Parameters:
47
+ - taskID: string (required)
48
+ ```
49
+
50
+ ### `explain_setup`
51
+ Provides information about what needs to be done to implement a feature based on task details.
52
+
53
+ ```
54
+ Parameters:
55
+ - taskID: string (required)
56
+ ```
57
+
58
+ ### `move_task`
59
+ Moves a task to a different board.
60
+
61
+ ```
62
+ Parameters:
63
+ - taskID: string (required)
64
+ - board: 'backlog' | 'in-progress' | 'reviewing' | 'completed' (required)
65
+ ```
66
+
67
+ ## Configuration in AI Tools
68
+
69
+ To use this MCP server with AI assistants that support MCP, add the following configuration:
70
+
71
+ ```json
72
+ "nubis": {
73
+ "command": "npx",
74
+ "args": [
75
+ "-y",
76
+ "@lil2good/nubis-mcp-server@latest",
77
+ "--workspaceID",
78
+ "your-workspace-id",
79
+ "--access-token",
80
+ "your-api-key"
81
+ ]
82
+ }
83
+ ```
84
+
85
+ ## Security
86
+
87
+ This MCP server uses a secure middleware architecture that keeps your API credentials safe. All privileged operations are performed through a secure server, while the MCP interface remains lightweight and secure for public distribution.
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/build/index.js ADDED
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
9
+ const zod_1 = require("zod");
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ dotenv_1.default.config();
12
+ // Register nubis tools
13
+ const cliWorkspaceID = process.argv.includes('--workspaceID')
14
+ ? process.argv[process.argv.indexOf('--workspaceID') + 1]
15
+ : undefined;
16
+ const cliApiKey = process.argv.includes('--apiKey')
17
+ ? process.argv[process.argv.indexOf('--apiKey') + 1]
18
+ : undefined;
19
+ // Create server instance
20
+ const server = new mcp_js_1.McpServer({
21
+ name: "nubis-mcp-server",
22
+ version: "1.0.0",
23
+ capabilities: {
24
+ resources: {},
25
+ tools: {},
26
+ },
27
+ });
28
+ // Helper to get results from middleware
29
+ async function getResultsFromMiddleware({ endpoint, schema }) {
30
+ const response = await fetch('https://mcp-server.nubis.app/' + endpoint, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ },
35
+ body: JSON.stringify({
36
+ workspaceId: cliWorkspaceID,
37
+ apiKey: cliApiKey,
38
+ schema: schema
39
+ }),
40
+ });
41
+ if (!response.ok) {
42
+ const errorData = await response.json();
43
+ throw new Error(errorData.error || 'Failed to fetch tasks from middleware');
44
+ }
45
+ const { data } = await response.json();
46
+ if (!data)
47
+ throw new Error('No data returned from middleware');
48
+ return data;
49
+ }
50
+ // Get Tasks -> Get tasks for a workspace
51
+ server.tool("get_tasks", "Get tasks for a workspace", {
52
+ limit: zod_1.z.number().optional().default(5),
53
+ board: zod_1.z.enum(['bugs', 'backlog', 'priority', 'in-progress', 'reviewing', 'completed']).optional(),
54
+ }, async ({ limit, board }) => {
55
+ try {
56
+ const data = await getResultsFromMiddleware({
57
+ endpoint: 'get_tasks',
58
+ schema: {
59
+ board,
60
+ limit
61
+ }
62
+ });
63
+ if (!data)
64
+ throw new Error('No data returned from middleware');
65
+ return {
66
+ content: data.map((task) => ({
67
+ type: "text",
68
+ text: [
69
+ `==============`,
70
+ `### ${task.title}`,
71
+ `**Task ID:** ${task.id}`,
72
+ `**Task Number:** ${task.task_number}`,
73
+ `**Board:** ${task.board}`,
74
+ `**Description:** ${task.description ? task.description : "_No description_"}`,
75
+ task.images && task.images.length > 0
76
+ ? task.images.map((image) => image.url).join('\n')
77
+ : "_No images_",
78
+ `==============`,
79
+ ].join('\n\n'),
80
+ })),
81
+ };
82
+ }
83
+ catch (error) {
84
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
85
+ throw new Error(errorMessage);
86
+ }
87
+ });
88
+ // Get Task -> Get a task by ID
89
+ server.tool("get_task", "Get a task by ID", {
90
+ taskID: zod_1.z.string(),
91
+ }, async ({ taskID }) => {
92
+ const data = await getResultsFromMiddleware({
93
+ endpoint: 'get_task',
94
+ schema: {
95
+ taskID
96
+ }
97
+ });
98
+ if (!data)
99
+ throw new Error('No data returned from middleware');
100
+ return {
101
+ content: [
102
+ {
103
+ type: "text",
104
+ text: JSON.stringify(data),
105
+ },
106
+ ],
107
+ };
108
+ });
109
+ // Get Task Images -> Get images for a task
110
+ server.tool("get_task_images", "Get/View images for a task", {
111
+ taskID: zod_1.z.string(),
112
+ }, async ({ taskID }) => {
113
+ const data = await getResultsFromMiddleware({
114
+ endpoint: 'get_task_images',
115
+ schema: {
116
+ taskID
117
+ }
118
+ });
119
+ if (!data)
120
+ throw new Error('No data returned from middleware');
121
+ return {
122
+ content: [
123
+ {
124
+ type: "text",
125
+ text: JSON.stringify(data),
126
+ },
127
+ ],
128
+ };
129
+ });
130
+ // Work on Task -> Work on a task
131
+ server.tool("work_on_task", "Work on a task", {
132
+ taskID: zod_1.z.string(),
133
+ }, async ({ taskID }) => {
134
+ const data = await getResultsFromMiddleware({
135
+ endpoint: 'work_on_task',
136
+ schema: {
137
+ taskID
138
+ }
139
+ });
140
+ if (!data)
141
+ throw new Error('No data returned from middleware');
142
+ return {
143
+ content: [
144
+ {
145
+ type: "text",
146
+ text: `You are assisting with task management in Nubis. Your task is to implement the user's requested action based on the following details:
147
+ \n **Task Instruction**: Process and update the task with the provided information.\n **Task ID**: ${taskID}\n **Task Details**: \n ${JSON.stringify(data, null, 2).replace(/"/g, '').replace(/:/g, ': ').replace(/},/g, ',\n')}\n \n Please analyze the details, perform the requested action (e.g., update description, add subtask), and return a response indicating the action taken.`
148
+ },
149
+ ],
150
+ };
151
+ });
152
+ // Explain setup and what user needs to do for feature to be implemented
153
+ server.tool("explain_setup", "Explain setup and what needs to be done for feature to be implemented", {
154
+ taskID: zod_1.z.string(),
155
+ }, async ({ taskID }) => {
156
+ const data = await getResultsFromMiddleware({
157
+ endpoint: 'explain_setup',
158
+ schema: {
159
+ taskID
160
+ }
161
+ });
162
+ if (!data)
163
+ throw new Error('No data returned from middleware');
164
+ return {
165
+ content: [
166
+ {
167
+ type: "text",
168
+ text: `You are assisting with task management in Nubis. Your task is to explain the setup and what needs to be done for feature to be implemented.\n \n **Task Instruction**: Explain the setup and what needs to be done for feature to be implemented.\n **Task Details**: \n ${JSON.stringify(data, null, 2).replace(/"/g, '').replace(/:/g, ': ').replace(/},/g, ',\n')}\n \n Please analyze the feature and return a response indicating the action that needs to be taken.`
169
+ },
170
+ ],
171
+ };
172
+ });
173
+ // Move Task -> Move a task to ['backlog', 'in-progress','reviewing', 'completed']
174
+ server.tool("move_task", "Move a task to ['backlog', 'in-progress','reviewing', 'completed']", {
175
+ taskID: zod_1.z.string(),
176
+ board: zod_1.z.enum(['backlog', 'in-progress', 'reviewing', 'completed']),
177
+ }, async ({ taskID, board }) => {
178
+ const data = await getResultsFromMiddleware({
179
+ endpoint: 'move_task',
180
+ schema: {
181
+ taskID,
182
+ board
183
+ }
184
+ });
185
+ if (!data)
186
+ throw new Error('No data returned from middleware');
187
+ return {
188
+ content: [
189
+ {
190
+ type: "text",
191
+ text: `Task ${taskID} has been moved to ${board}`,
192
+ },
193
+ ],
194
+ };
195
+ });
196
+ // Start server
197
+ async function main() {
198
+ const transport = new stdio_js_1.StdioServerTransport();
199
+ await server.connect(transport);
200
+ console.error("Nubis MCP Server running on stdio");
201
+ }
202
+ main().catch((error) => {
203
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
204
+ console.error("Fatal error in main():", errorMessage);
205
+ process.exit(1);
206
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@lil2good/nubis-mcp-server",
3
+ "version": "1.0.8",
4
+ "description": "MCP server for Nubis task management",
5
+ "main": "build/index.js",
6
+ "bin": {
7
+ "nubis-mcp-server": "build/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "cd server && npm run build",
11
+ "start": "cd server && npm start",
12
+ "dev": "cd server && npm run dev",
13
+ "mcp:build": "tsc",
14
+ "mcp:start": "node build/index.js",
15
+ "mcp:dev": "ts-node-dev --respawn --transpile-only src/index.ts"
16
+ },
17
+ "author": "lil2good",
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.9.0",
21
+ "dotenv": "^16.0.0",
22
+ "zod": "^3.24.2"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^20.11.30",
26
+ "ts-node-dev": "^2.0.0",
27
+ "typescript": "^5.4.5"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "files": [
33
+ "build/",
34
+ "src/",
35
+ "README.md",
36
+ "package.json",
37
+ "tsconfig.json"
38
+ ]
39
+ }
package/src/index.ts ADDED
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+ import dotenv from 'dotenv';
7
+
8
+ dotenv.config();
9
+
10
+ // Register nubis tools
11
+ const cliWorkspaceID = process.argv.includes('--workspaceID')
12
+ ? process.argv[process.argv.indexOf('--workspaceID') + 1]
13
+ : undefined;
14
+
15
+ const cliApiKey = process.argv.includes('--apiKey')
16
+ ? process.argv[process.argv.indexOf('--apiKey') + 1]
17
+ : undefined;
18
+
19
+ // Create server instance
20
+ const server = new McpServer({
21
+ name: "nubis-mcp-server",
22
+ version: "1.0.0",
23
+ capabilities: {
24
+ resources: {},
25
+ tools: {},
26
+ },
27
+ });
28
+
29
+ // Helper to get results from middleware
30
+ async function getResultsFromMiddleware({endpoint, schema}: {endpoint: string, schema: any}) {
31
+ const response = await fetch('https://mcp-server.nubis.app/' + endpoint, {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ body: JSON.stringify({
37
+ workspaceId: cliWorkspaceID,
38
+ apiKey: cliApiKey,
39
+ schema: schema
40
+ }),
41
+ });
42
+
43
+ if (!response.ok) {
44
+ const errorData = await response.json();
45
+ throw new Error(errorData.error || 'Failed to fetch tasks from middleware');
46
+ }
47
+
48
+ const { data } = await response.json();
49
+ if (!data) throw new Error('No data returned from middleware');
50
+ return data;
51
+ }
52
+
53
+ // Get Tasks -> Get tasks for a workspace
54
+ server.tool(
55
+ "get_tasks",
56
+ "Get tasks for a workspace",
57
+ {
58
+ limit: z.number().optional().default(5),
59
+ board: z.enum(['bugs', 'backlog', 'priority', 'in-progress', 'reviewing', 'completed']).optional(),
60
+ },
61
+ async ({ limit, board }) => {
62
+ try {
63
+ const data = await getResultsFromMiddleware({
64
+ endpoint: 'get_tasks',
65
+ schema: {
66
+ board,
67
+ limit
68
+ }
69
+ });
70
+ if (!data) throw new Error('No data returned from middleware');
71
+
72
+ /**
73
+ * Formats a list of tasks into Markdown content.
74
+ * @param data - Array of Task objects to format.
75
+ * @returns An object with content as an array of formatted Markdown strings.
76
+ */
77
+ type Task = {
78
+ readonly id: string;
79
+ readonly title: string;
80
+ readonly task_number: number;
81
+ readonly board: string;
82
+ readonly description?: string;
83
+ readonly images?: readonly { url: string }[];
84
+ };
85
+ return {
86
+ content: (data as Task[]).map((task) => ({
87
+ type: "text",
88
+ text: [
89
+ `==============`,
90
+ `### ${task.title}`,
91
+ `**Task ID:** ${task.id}`,
92
+ `**Task Number:** ${task.task_number}`,
93
+ `**Board:** ${task.board}`,
94
+ `**Description:** ${task.description ? task.description : "_No description_"}`,
95
+ task.images && task.images.length > 0
96
+ ? task.images.map((image) => image.url).join('\n')
97
+ : "_No images_",
98
+ `==============`,
99
+ ].join('\n\n'),
100
+ })),
101
+ };
102
+ } catch (error: unknown) {
103
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
104
+ throw new Error(errorMessage);
105
+ }
106
+ }
107
+ );
108
+
109
+ // Get Task -> Get a task by ID
110
+ server.tool(
111
+ "get_task",
112
+ "Get a task by ID",
113
+ {
114
+ taskID: z.string(),
115
+ },
116
+ async ({ taskID }) => {
117
+ const data = await getResultsFromMiddleware({
118
+ endpoint: 'get_task',
119
+ schema: {
120
+ taskID
121
+ }
122
+ });
123
+ if (!data) throw new Error('No data returned from middleware');
124
+ return {
125
+ content: [
126
+ {
127
+ type: "text",
128
+ text: JSON.stringify(data),
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ );
134
+
135
+ // Get Task Images -> Get images for a task
136
+ server.tool(
137
+ "get_task_images",
138
+ "Get/View images for a task",
139
+ {
140
+ taskID: z.string(),
141
+ },
142
+ async ({ taskID }) => {
143
+ const data = await getResultsFromMiddleware({
144
+ endpoint: 'get_task_images',
145
+ schema: {
146
+ taskID
147
+ }
148
+ });
149
+ if (!data) throw new Error('No data returned from middleware');
150
+ return {
151
+ content: [
152
+ {
153
+ type: "text",
154
+ text: JSON.stringify(data),
155
+ },
156
+ ],
157
+ };
158
+ }
159
+ );
160
+
161
+ // Work on Task -> Work on a task
162
+ server.tool(
163
+ "work_on_task",
164
+ "Work on a task",
165
+ {
166
+ taskID: z.string(),
167
+ },
168
+ async ({ taskID }) => {
169
+ const data = await getResultsFromMiddleware({
170
+ endpoint: 'work_on_task',
171
+ schema: {
172
+ taskID
173
+ }
174
+ });
175
+ if (!data) throw new Error('No data returned from middleware');
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: `You are assisting with task management in Nubis. Your task is to implement the user's requested action based on the following details:
181
+ \n **Task Instruction**: Process and update the task with the provided information.\n **Task ID**: ${taskID}\n **Task Details**: \n ${JSON.stringify(data, null, 2).replace(/"/g, '').replace(/:/g, ': ').replace(/},/g, ',\n')}\n \n Please analyze the details, perform the requested action (e.g., update description, add subtask), and return a response indicating the action taken.`
182
+ },
183
+ ],
184
+ };
185
+ }
186
+ );
187
+
188
+ // Explain setup and what user needs to do for feature to be implemented
189
+ server.tool(
190
+ "explain_setup",
191
+ "Explain setup and what needs to be done for feature to be implemented",
192
+ {
193
+ taskID: z.string(),
194
+ },
195
+ async ({ taskID }) => {
196
+ const data = await getResultsFromMiddleware({
197
+ endpoint: 'explain_setup',
198
+ schema: {
199
+ taskID
200
+ }
201
+ });
202
+
203
+ if (!data) throw new Error('No data returned from middleware');
204
+ return {
205
+ content: [
206
+ {
207
+ type: "text",
208
+ text: `You are assisting with task management in Nubis. Your task is to explain the setup and what needs to be done for feature to be implemented.\n \n **Task Instruction**: Explain the setup and what needs to be done for feature to be implemented.\n **Task Details**: \n ${JSON.stringify(data, null, 2).replace(/"/g, '').replace(/:/g, ': ').replace(/},/g, ',\n')}\n \n Please analyze the feature and return a response indicating the action that needs to be taken.`
209
+ },
210
+ ],
211
+ };
212
+ }
213
+ );
214
+
215
+ // Move Task -> Move a task to ['backlog', 'in-progress','reviewing', 'completed']
216
+ server.tool(
217
+ "move_task",
218
+ "Move a task to ['backlog', 'in-progress','reviewing', 'completed']",
219
+ {
220
+ taskID: z.string(),
221
+ board: z.enum(['backlog', 'in-progress', 'reviewing', 'completed']),
222
+ },
223
+ async ({ taskID, board }) => {
224
+ const data = await getResultsFromMiddleware({
225
+ endpoint: 'move_task',
226
+ schema: {
227
+ taskID,
228
+ board
229
+ }
230
+ });
231
+ if (!data) throw new Error('No data returned from middleware');
232
+ return {
233
+ content: [
234
+ {
235
+ type: "text",
236
+ text: `Task ${taskID} has been moved to ${board}`,
237
+ },
238
+ ],
239
+ };
240
+ }
241
+ );
242
+
243
+ // Start server
244
+ async function main() {
245
+ const transport = new StdioServerTransport();
246
+ await server.connect(transport);
247
+ console.error("Nubis MCP Server running on stdio");
248
+ }
249
+
250
+ main().catch((error: unknown) => {
251
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
252
+ console.error("Fatal error in main():", errorMessage);
253
+ process.exit(1);
254
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "./build",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules"]
15
+ }