zephyr-enterprise-tools 1.2.1 → 1.2.3

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 CHANGED
@@ -73,50 +73,46 @@ Use zephyr-enterprise-tools as an MCP (Model Context Protocol) server with your
73
73
  | `search_test_cases` | Search test cases by query |
74
74
  | `user_activity` | Get user activity metrics |
75
75
 
76
- ### VS Code with GitHub Copilot
76
+ ### Claude Desktop
77
77
 
78
- Create or edit `.vscode/mcp.json` in your workspace:
78
+ Edit your `claude_desktop_config.json` file:
79
79
 
80
80
  ```json
81
81
  {
82
- "servers": {
83
- "zephyr-enterprise": {
84
- "type": "stdio",
82
+ "mcpServers": {
83
+ "zephyr": {
85
84
  "command": "npx",
86
- "args": ["-y", "zephyr-enterprise-tools@latest", "mcp"],
85
+ "args": [
86
+ "-y",
87
+ "--package",
88
+ "zephyr-enterprise-tools@latest",
89
+ "zephyr-mcp"
90
+ ],
87
91
  "env": {
88
- "ZEPHYR_BASE_URL": "${input:zephyr_base_url}",
89
- "ZEPHYR_TOKEN": "${input:zephyr_token}"
92
+ "ZEPHYR_BASE_URL": "https://your-zephyr.com/flex/services/rest/latest",
93
+ "ZEPHYR_TOKEN": "your-api-token"
90
94
  }
91
95
  }
92
- },
93
- "inputs": [
94
- {
95
- "id": "zephyr_base_url",
96
- "type": "promptString",
97
- "description": "Zephyr Enterprise API Base URL (e.g. https://your-zephyr.com/flex/services/rest/latest)",
98
- "password": false
99
- },
100
- {
101
- "id": "zephyr_token",
102
- "type": "promptString",
103
- "description": "Zephyr Enterprise API Token",
104
- "password": true
105
- }
106
- ]
96
+ }
107
97
  }
108
98
  ```
109
99
 
110
- ### Cursor
100
+ ### VS Code with GitHub Copilot
111
101
 
112
- Add to your `mcp.json` configuration:
102
+ Create or edit `.vscode/mcp.json` in your workspace:
113
103
 
114
104
  ```json
115
105
  {
116
- "mcpServers": {
117
- "zephyr-enterprise": {
106
+ "servers": {
107
+ "zephyr": {
108
+ "type": "stdio",
118
109
  "command": "npx",
119
- "args": ["-y", "zephyr-enterprise-tools@latest", "mcp"],
110
+ "args": [
111
+ "-y",
112
+ "--package",
113
+ "zephyr-enterprise-tools",
114
+ "zephyr-mcp"
115
+ ],
120
116
  "env": {
121
117
  "ZEPHYR_BASE_URL": "https://your-zephyr.com/flex/services/rest/latest",
122
118
  "ZEPHYR_TOKEN": "your-api-token"
@@ -126,9 +122,9 @@ Add to your `mcp.json` configuration:
126
122
  }
127
123
  ```
128
124
 
129
- ### Claude Desktop
125
+ ### Cursor
130
126
 
131
- Edit your `claude_desktop_config.json` file:
127
+ Add to your `mcp.json` configuration:
132
128
 
133
129
  ```json
134
130
  {
package/mcp-server.js CHANGED
@@ -167,6 +167,23 @@ const TOOLS = [
167
167
  required: ['projectId', 'releaseId'],
168
168
  },
169
169
  },
170
+ {
171
+ name: 'user_trend',
172
+ description: 'Get full audit log activity for a user — every action they performed across the system. Filter by date range and optionally by entity type (project or release).',
173
+ inputSchema: {
174
+ type: 'object',
175
+ properties: {
176
+ userName: { type: 'string', description: 'Username to look up (e.g. "dylan.garcia")' },
177
+ fromDate: { type: 'string', description: 'Start date YYYY-MM-DD (e.g. "2026-08-01")' },
178
+ toDate: { type: 'string', description: 'End date YYYY-MM-DD (e.g. "2026-08-18")' },
179
+ entity: { type: 'string', description: 'Filter by entity type: "project", "release", or "" for all', enum: ['project', 'release', ''] },
180
+ operation: { type: 'string', description: 'Filter by operation (e.g. "CREATE", "UPDATE"). Omit for all.' },
181
+ offset: { type: 'number', description: 'Pagination offset (default 0)' },
182
+ pageSize: { type: 'number', description: 'Records per page (default 25)' },
183
+ },
184
+ required: ['userName'],
185
+ },
186
+ },
170
187
  {
171
188
  name: 'list_projects',
172
189
  description: 'List all available Zephyr projects.',
@@ -282,6 +299,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
282
299
  case 'user_activity':
283
300
  result = await tools.getUserActivity(projectId, releaseId, { days: args.days || 30 });
284
301
  break;
302
+
303
+ case 'user_trend':
304
+ result = await tools.getUserTrend({
305
+ userName: args.userName,
306
+ fromDate: args.fromDate || null,
307
+ toDate: args.toDate || null,
308
+ entity: args.entity || '',
309
+ operation: args.operation || null,
310
+ offset: args.offset || 0,
311
+ pageSize: args.pageSize || 25,
312
+ });
313
+ break;
285
314
 
286
315
  case 'list_projects':
287
316
  const projects = await tools.GET('/project');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zephyr-enterprise-tools",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Comprehensive Zephyr Enterprise Tools - Release Readiness, Project Health, Test Analytics & More",
5
5
  "main": "zephyr-enterprise-tools.js",
6
6
  "types": "zephyr-enterprise-tools.d.ts",
@@ -1285,6 +1285,99 @@ export class QualityGates {
1285
1285
  topExecutors: executors.slice(0, 5),
1286
1286
  };
1287
1287
  }
1288
+
1289
+ // ─── User Trend (Audit Logs) ─────────────────────────────────────────────────
1290
+
1291
+ async getUserTrend(options = {}) {
1292
+ const {
1293
+ userName = "",
1294
+ fromDate = null, // "YYYY-MM-DD"
1295
+ toDate = null, // "YYYY-MM-DD"
1296
+ entity = "", // "project", "release", or "" for all
1297
+ operation = null, // specific operation or null for all
1298
+ offset = 0,
1299
+ pageSize = 25,
1300
+ } = options;
1301
+
1302
+ const v3BaseUrl = this.baseUrl.replace('/latest', '/v3');
1303
+ const url = new URL(`${v3BaseUrl}/ui/auditLogs`);
1304
+ url.searchParams.set("offset", String(offset));
1305
+ url.searchParams.set("pagesize", String(pageSize));
1306
+ url.searchParams.set("isascorder", "false");
1307
+ url.searchParams.set("order", "id");
1308
+ url.searchParams.set("isExport", "false");
1309
+
1310
+ const body = { entity, operation, userName };
1311
+
1312
+ if (fromDate) {
1313
+ body.fromDate = fromDate;
1314
+ body.fromDateVal = `${fromDate}T00:00:00.000Z`;
1315
+ }
1316
+ if (toDate) {
1317
+ body.toDate = toDate;
1318
+ body.toDateVal = `${toDate}T00:00:00.000Z`;
1319
+ }
1320
+
1321
+ const res = await fetch(url.toString(), {
1322
+ method: "POST",
1323
+ headers: {
1324
+ Accept: "application/json",
1325
+ "Content-Type": "application/json",
1326
+ ...this.authHeader(),
1327
+ },
1328
+ body: JSON.stringify(body),
1329
+ });
1330
+
1331
+ if (!res.ok) {
1332
+ const text = await res.text().catch(() => "");
1333
+ throw new Error(`Zephyr audit logs API ${res.status}: ${text}`);
1334
+ }
1335
+
1336
+ const data = await res.json();
1337
+ const logs = data.results || data || [];
1338
+
1339
+ // Summarise by operation and entity type
1340
+ const byOperation = {};
1341
+ const byEntity = {};
1342
+ for (const log of logs) {
1343
+ const op = log.operation || "Unknown";
1344
+ const ent = log.entity || "Unknown";
1345
+ byOperation[op] = (byOperation[op] || 0) + 1;
1346
+ byEntity[ent] = (byEntity[ent] || 0) + 1;
1347
+ }
1348
+
1349
+ return {
1350
+ tool: "User Trend",
1351
+ userName,
1352
+ dateRange: { from: fromDate || "all", to: toDate || "all" },
1353
+ entity: entity || "all",
1354
+ timestamp: new Date().toISOString(),
1355
+ totalRecords: data.totalCount ?? logs.length,
1356
+ offset,
1357
+ pageSize,
1358
+ operationSummary: Object.entries(byOperation)
1359
+ .map(([operation, count]) => ({ operation, count }))
1360
+ .sort((a, b) => b.count - a.count),
1361
+ entitySummary: Object.entries(byEntity)
1362
+ .map(([entity, count]) => ({ entity, count }))
1363
+ .sort((a, b) => b.count - a.count),
1364
+ auditLogs: logs.map(log => ({
1365
+ id: log.id,
1366
+ userName: log.userName,
1367
+ entity: log.entity,
1368
+ entityName: log.entityName,
1369
+ operation: log.operation,
1370
+ description: log.description,
1371
+ detail: log.detail,
1372
+ projectId: log.projectId,
1373
+ projectName: log.projectName,
1374
+ releaseId: log.releaseId,
1375
+ releaseName: log.releaseName,
1376
+ createdOn: log.createdOn,
1377
+ ipAddress: log.ipAddress,
1378
+ })),
1379
+ };
1380
+ }
1288
1381
  }
1289
1382
 
1290
1383
  // Export with both names for backward compatibility