@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 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: "1.2.5"
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: JSON.stringify(cleaned, null, 2) }],
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: JSON.stringify(cleaned, null, 2) }],
204
+ content: [{ type: "text", text: bugToMarkdown(cleaned) }],
132
205
  };
133
206
  });
134
207
  server.registerTool("zentao_download_attachment", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyno181cm.nexsoft/zentao_mcp",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Zentao MCP Server",
5
5
  "repository": {
6
6
  "type": "git",