@dyno181cm.nexsoft/zentao_mcp 1.2.6 → 1.2.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/build/index.js +4 -1
- package/build/tools.js +75 -2
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -3,12 +3,15 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { registerTools } from "./tools.js";
|
|
5
5
|
import * as dotenv from "dotenv";
|
|
6
|
+
import { readFileSync } from "fs";
|
|
6
7
|
// Initialize environment variables from .env file
|
|
7
8
|
dotenv.config();
|
|
9
|
+
// Read package.json to get the version dynamically
|
|
10
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
8
11
|
// Create the MCP server instance
|
|
9
12
|
const server = new McpServer({
|
|
10
13
|
name: "Zentao MCP Server",
|
|
11
|
-
version:
|
|
14
|
+
version: pkg.version
|
|
12
15
|
});
|
|
13
16
|
// Register all Zentao-specific tools onto the server
|
|
14
17
|
registerTools(server);
|
package/build/tools.js
CHANGED
|
@@ -106,6 +106,79 @@ export function cleanBug(bug) {
|
|
|
106
106
|
files: parseFiles(bug.files)
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
|
+
export function formatSize(bytes) {
|
|
110
|
+
if (bytes === undefined || bytes === null || bytes === '')
|
|
111
|
+
return 'unknown size';
|
|
112
|
+
const numBytes = typeof bytes === 'string' ? parseInt(bytes, 10) : bytes;
|
|
113
|
+
if (isNaN(numBytes))
|
|
114
|
+
return 'unknown size';
|
|
115
|
+
if (numBytes < 1024)
|
|
116
|
+
return `${numBytes} B`;
|
|
117
|
+
if (numBytes < 1024 * 1024)
|
|
118
|
+
return `${(numBytes / 1024).toFixed(2)} KB`;
|
|
119
|
+
return `${(numBytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
120
|
+
}
|
|
121
|
+
export function taskToMarkdown(task) {
|
|
122
|
+
if (!task)
|
|
123
|
+
return "Task not found.";
|
|
124
|
+
let md = `# Task #${task.id}: ${task.name}\n\n`;
|
|
125
|
+
md += `- **Status**: ${task.status || 'N/A'}\n`;
|
|
126
|
+
md += `- **Priority**: ${task.pri || 'N/A'}\n`;
|
|
127
|
+
const est = task.estimate ?? 0;
|
|
128
|
+
const cons = task.consumed ?? 0;
|
|
129
|
+
const left = task.left ?? 0;
|
|
130
|
+
const prog = task.progress ?? 0;
|
|
131
|
+
md += `- **Estimate / Consumed / Left**: ${est}h / ${cons}h / ${left}h (${prog}%)\n`;
|
|
132
|
+
if (task.openedBy)
|
|
133
|
+
md += `- **Opened By**: ${task.openedBy.realname} (${task.openedBy.account})\n`;
|
|
134
|
+
if (task.assignedTo)
|
|
135
|
+
md += `- **Assigned To**: ${task.assignedTo.realname} (${task.assignedTo.account})\n`;
|
|
136
|
+
if (task.finishedBy)
|
|
137
|
+
md += `- **Finished By**: ${task.finishedBy.realname} (${task.finishedBy.account})\n`;
|
|
138
|
+
if (task.closedBy) {
|
|
139
|
+
md += `- **Closed By**: ${task.closedBy.realname} (${task.closedBy.account})`;
|
|
140
|
+
if (task.closedReason)
|
|
141
|
+
md += ` (Reason: ${task.closedReason})`;
|
|
142
|
+
md += `\n`;
|
|
143
|
+
}
|
|
144
|
+
md += `\n## Description\n${task.desc || '*No description provided.*'}\n`;
|
|
145
|
+
if (task.files && task.files.length > 0) {
|
|
146
|
+
md += `\n## Attachments\n`;
|
|
147
|
+
task.files.forEach((f) => {
|
|
148
|
+
md += `- 📎 **${f.title}** (ID: ${f.id}, Extension: ${f.extension}, Size: ${formatSize(f.size)})\n`;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return md;
|
|
152
|
+
}
|
|
153
|
+
export function bugToMarkdown(bug) {
|
|
154
|
+
if (!bug)
|
|
155
|
+
return "Bug not found.";
|
|
156
|
+
let md = `# Bug #${bug.id}: ${bug.title}\n\n`;
|
|
157
|
+
md += `- **Status**: ${bug.status || 'N/A'}\n`;
|
|
158
|
+
md += `- **Severity**: ${bug.severity || 'N/A'}\n`;
|
|
159
|
+
md += `- **Priority**: ${bug.pri || 'N/A'}\n`;
|
|
160
|
+
md += `- **Type**: ${bug.type || 'N/A'}\n`;
|
|
161
|
+
if (bug.openedBy)
|
|
162
|
+
md += `- **Opened By**: ${bug.openedBy.realname} (${bug.openedBy.account})\n`;
|
|
163
|
+
if (bug.assignedTo)
|
|
164
|
+
md += `- **Assigned To**: ${bug.assignedTo.realname} (${bug.assignedTo.account})\n`;
|
|
165
|
+
if (bug.resolvedBy) {
|
|
166
|
+
md += `- **Resolved By**: ${bug.resolvedBy.realname} (${bug.resolvedBy.account})`;
|
|
167
|
+
if (bug.resolution)
|
|
168
|
+
md += ` (Resolution: ${bug.resolution})`;
|
|
169
|
+
md += `\n`;
|
|
170
|
+
}
|
|
171
|
+
if (bug.closedBy)
|
|
172
|
+
md += `- **Closed By**: ${bug.closedBy.realname} (${bug.closedBy.account})\n`;
|
|
173
|
+
md += `\n## Steps to Reproduce\n${bug.steps || '*No steps provided.*'}\n`;
|
|
174
|
+
if (bug.files && bug.files.length > 0) {
|
|
175
|
+
md += `\n## Attachments\n`;
|
|
176
|
+
bug.files.forEach((f) => {
|
|
177
|
+
md += `- 📎 **${f.title}** (ID: ${f.id}, Extension: ${f.extension}, Size: ${formatSize(f.size)})\n`;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return md;
|
|
181
|
+
}
|
|
109
182
|
export function registerTools(server) {
|
|
110
183
|
server.registerTool("zentao_get_task_details", {
|
|
111
184
|
description: "Get detailed information of a task",
|
|
@@ -116,7 +189,7 @@ export function registerTools(server) {
|
|
|
116
189
|
const data = await client.getTaskDetails(taskId);
|
|
117
190
|
const cleaned = cleanTask(data);
|
|
118
191
|
return {
|
|
119
|
-
content: [{ type: "text", text:
|
|
192
|
+
content: [{ type: "text", text: taskToMarkdown(cleaned) }],
|
|
120
193
|
};
|
|
121
194
|
});
|
|
122
195
|
server.registerTool("zentao_get_bug_details", {
|
|
@@ -128,7 +201,7 @@ export function registerTools(server) {
|
|
|
128
201
|
const data = await client.getBugDetails(bugId);
|
|
129
202
|
const cleaned = cleanBug(data);
|
|
130
203
|
return {
|
|
131
|
-
content: [{ type: "text", text:
|
|
204
|
+
content: [{ type: "text", text: bugToMarkdown(cleaned) }],
|
|
132
205
|
};
|
|
133
206
|
});
|
|
134
207
|
server.registerTool("zentao_download_attachment", {
|