@dpesch/mantisbt-mcp-server 1.6.1 → 1.6.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ This project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.6.2] – 2026-03-24
11
+
12
+ ### Fixed
13
+ - `create_issue`: default priority is now `"normal"`, matching the MantisBT UI default. Previously no priority was sent when omitted, causing MantisBT to fall back to its server-side default of `"low"`.
14
+ - `create_issue`: `description` is now a required field (minimum 1 character). Previously it defaulted to an empty string, which caused the MantisBT API to reject the request. The validation error is now surfaced immediately by the MCP server before the API is called.
15
+
16
+ ### Changed
17
+ - npm keywords extended: added `issue-tracker`, `bug-tracker`, `claude`, `claude-code` for better discoverability.
18
+
19
+ ---
20
+
10
21
  ## [1.6.1] – 2026-03-24
11
22
 
12
23
  ### Changed
@@ -144,10 +144,10 @@ export function registerIssueTools(server, client, cache) {
144
144
  description: 'Create a new MantisBT issue. Returns the created issue including its assigned ID.',
145
145
  inputSchema: z.object({
146
146
  summary: z.string().min(1).describe('Issue summary/title'),
147
- description: z.string().default('').describe('Detailed issue description'),
147
+ description: z.string().min(1).describe('Detailed issue description. Required — do not create issues without a description. Plain text or Markdown.'),
148
148
  project_id: z.coerce.number().int().positive().describe('Project ID the issue belongs to'),
149
149
  category: z.string().min(1).describe('Category name (use get_project_categories to list available categories)'),
150
- priority: z.string().optional().describe('Priority name — must be a canonical English name: none, low, normal, high, urgent, immediate. Call get_issue_enums to see localized labels.'),
150
+ priority: z.string().default('normal').describe('Priority name — must be a canonical English name: none, low, normal, high, urgent, immediate. Default: "normal". Call get_issue_enums to see localized labels.'),
151
151
  severity: z.string().default('minor').describe('Severity name — must be a canonical English name: feature, trivial, text, tweak, minor, major, crash, block. Default: "minor". Call get_issue_enums to see localized labels.'),
152
152
  handler_id: z.coerce.number().int().positive().optional().describe('User ID of the person to assign the issue to'),
153
153
  handler: z.string().optional().describe('Username (login name) of the person to assign the issue to. Alternative to handler_id — the server resolves the name to a user ID from the project members. Use get_project_users to see available users.'),
@@ -189,12 +189,10 @@ export function registerIssueTools(server, client, cache) {
189
189
  project: { id: project_id },
190
190
  category: { name: category },
191
191
  };
192
- if (priority) {
193
- const priorityResolved = resolveEnum('priority', priority);
194
- if (typeof priorityResolved === 'string')
195
- return { content: [{ type: 'text', text: errorText(priorityResolved) }], isError: true };
196
- body.priority = priorityResolved;
197
- }
192
+ const priorityResolved = resolveEnum('priority', priority);
193
+ if (typeof priorityResolved === 'string')
194
+ return { content: [{ type: 'text', text: errorText(priorityResolved) }], isError: true };
195
+ body.priority = priorityResolved;
198
196
  const severityResolved = resolveEnum('severity', severity);
199
197
  if (typeof severityResolved === 'string')
200
198
  return { content: [{ type: 'text', text: errorText(severityResolved) }], isError: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dpesch/mantisbt-mcp-server",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "mcpName": "io.github.dpesch/mantisbt-mcp-server",
5
5
  "description": "MCP server for MantisBT REST API – read and manage bug tracker issues",
6
6
  "author": "Dominik Pesch",
@@ -14,7 +14,7 @@
14
14
  "url": "https://github.com/dpesch/mantisbt-mcp-server",
15
15
  "_note": "GitHub mirror for ecosystem compatibility only — canonical source: https://codeberg.org/dpesch/mantisbt-mcp-server"
16
16
  },
17
- "keywords": ["mcp", "mcp-server", "mantisbt", "bugtracker", "mantis", "model-context-protocol"],
17
+ "keywords": ["mcp", "mcp-server", "mantisbt", "bugtracker", "mantis", "issue-tracker", "bug-tracker", "model-context-protocol", "claude", "claude-code"],
18
18
  "main": "dist/index.js",
19
19
  "bin": {
20
20
  "mantisbt-mcp-server": "dist/index.js"
@@ -166,9 +166,17 @@ rl.on('close', () => {
166
166
  shaMap[sha] = makeFilteredCommit(sha, filteredTree, mappedParents);
167
167
  }
168
168
  } else {
169
- // Fallback: anchor not found — filter tip only, rooted at remote tip.
169
+ // Fallback: anchor not found.
170
+ // If the remote SHA doesn't exist locally (filtered commit from a prior push),
171
+ // tags can be safely skipped (immutable — already correct on Codeberg).
172
+ // Branches fall back to orphan filtering (best effort).
173
+ const remoteExists = !!gitOptional(`rev-parse --verify "${actualRemoteSha}"`);
174
+ if (!remoteExists && remoteRef.startsWith('refs/tags/')) {
175
+ console.log(` ↷ ${label} already on Codeberg — skipping`);
176
+ continue;
177
+ }
170
178
  console.warn(` ⚠ Could not find local base for ${label}, filtering tip only`);
171
- shaMap[localSha] = makeFilteredCommit(localSha, filterTree(localSha), [actualRemoteSha]);
179
+ shaMap[localSha] = makeFilteredCommit(localSha, filterTree(localSha), remoteExists ? [actualRemoteSha] : []);
172
180
  }
173
181
  }
174
182
 
package/server.json CHANGED
@@ -3,12 +3,12 @@
3
3
  "name": "io.github.dpesch/mantisbt-mcp-server",
4
4
  "title": "MantisBT MCP Server",
5
5
  "description": "MantisBT MCP server – manage issues, notes, files, tags, and relationships. With semantic search.",
6
- "version": "1.6.1",
6
+ "version": "1.6.2",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "@dpesch/mantisbt-mcp-server",
11
- "version": "1.6.1",
11
+ "version": "1.6.2",
12
12
  "runtimeHint": "npx",
13
13
  "transport": {
14
14
  "type": "stdio"
@@ -123,6 +123,7 @@ describe('create_issue', () => {
123
123
 
124
124
  await mockServer.callTool('create_issue', {
125
125
  summary: 'Test issue',
126
+ description: 'Test description.',
126
127
  project_id: 1,
127
128
  category: 'General',
128
129
  }, { validate: true });
@@ -138,7 +139,7 @@ describe('create_issue', () => {
138
139
  );
139
140
 
140
141
  const result = await mockServer.callTool('create_issue', {
141
- summary: 'New issue', project_id: 1, category: 'General',
142
+ summary: 'New issue', description: 'New issue description.', project_id: 1, category: 'General',
142
143
  }, { validate: true });
143
144
 
144
145
  expect(result.isError).toBeUndefined();
@@ -156,7 +157,7 @@ describe('create_issue', () => {
156
157
  .mockResolvedValueOnce(makeResponse(200, JSON.stringify({ issues: [fullIssue] })));
157
158
 
158
159
  const result = await mockServer.callTool('create_issue', {
159
- summary: 'Created issue', project_id: 1, category: 'General',
160
+ summary: 'Created issue', description: 'Created issue description.', project_id: 1, category: 'General',
160
161
  }, { validate: true });
161
162
 
162
163
  expect(result.isError).toBeUndefined();
@@ -174,7 +175,7 @@ describe('create_issue', () => {
174
175
  .mockResolvedValueOnce(makeResponse(500, 'Server Error'));
175
176
 
176
177
  const result = await mockServer.callTool('create_issue', {
177
- summary: 'Test', project_id: 1, category: 'General',
178
+ summary: 'Test', description: 'Test description.', project_id: 1, category: 'General',
178
179
  }, { validate: true });
179
180
 
180
181
  expect(result.isError).toBeUndefined();
@@ -189,6 +190,7 @@ describe('create_issue', () => {
189
190
 
190
191
  await mockServer.callTool('create_issue', {
191
192
  summary: 'Crash bug',
193
+ description: 'Crash bug description.',
192
194
  project_id: 1,
193
195
  category: 'General',
194
196
  severity: 'crash',
@@ -198,9 +200,33 @@ describe('create_issue', () => {
198
200
  expect(body.severity).toEqual({ id: 70 });
199
201
  });
200
202
 
203
+ it('returns a validation error when description is missing', async () => {
204
+ const result = await mockServer.callTool('create_issue', {
205
+ summary: 'Test',
206
+ project_id: 1,
207
+ category: 'General',
208
+ }, { validate: true });
209
+
210
+ expect(result.isError).toBe(true);
211
+ expect(fetch).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it('returns a validation error when description is empty', async () => {
215
+ const result = await mockServer.callTool('create_issue', {
216
+ summary: 'Test',
217
+ description: '',
218
+ project_id: 1,
219
+ category: 'General',
220
+ }, { validate: true });
221
+
222
+ expect(result.isError).toBe(true);
223
+ expect(fetch).not.toHaveBeenCalled();
224
+ });
225
+
201
226
  it('returns an error for an unknown severity name', async () => {
202
227
  const result = await mockServer.callTool('create_issue', {
203
228
  summary: 'Test',
229
+ description: 'Test description.',
204
230
  project_id: 1,
205
231
  category: 'General',
206
232
  severity: 'schwerer Fehler',
@@ -228,7 +254,7 @@ describe('create_issue – handler username', () => {
228
254
  );
229
255
 
230
256
  await server.callTool('create_issue', {
231
- summary: 'Test', project_id: 1, category: 'General', handler: 'dom',
257
+ summary: 'Test', description: 'Test description.', project_id: 1, category: 'General', handler: 'dom',
232
258
  }, { validate: true });
233
259
 
234
260
  const body = JSON.parse(vi.mocked(fetch).mock.calls[0]![1]!.body as string) as { handler: { id: number } };
@@ -245,7 +271,7 @@ describe('create_issue – handler username', () => {
245
271
  );
246
272
 
247
273
  await server.callTool('create_issue', {
248
- summary: 'Test', project_id: 1, category: 'General', handler: 'John Doe',
274
+ summary: 'Test', description: 'Test description.', project_id: 1, category: 'General', handler: 'John Doe',
249
275
  }, { validate: true });
250
276
 
251
277
  const body = JSON.parse(vi.mocked(fetch).mock.calls[0]![1]!.body as string) as { handler: { id: number } };
@@ -262,7 +288,7 @@ describe('create_issue – handler username', () => {
262
288
  .mockResolvedValueOnce(makeResponse(201, JSON.stringify({ issue: { id: 202, summary: 'New' } })));
263
289
 
264
290
  await server.callTool('create_issue', {
265
- summary: 'Test', project_id: 1, category: 'General', handler: 'alice',
291
+ summary: 'Test', description: 'Test description.', project_id: 1, category: 'General', handler: 'alice',
266
292
  }, { validate: true });
267
293
 
268
294
  const projectUsersCall = vi.mocked(fetch).mock.calls[0]![0] as string;
@@ -278,7 +304,7 @@ describe('create_issue – handler username', () => {
278
304
  registerIssueTools(server as never, client, cache);
279
305
 
280
306
  const result = await server.callTool('create_issue', {
281
- summary: 'Test', project_id: 1, category: 'General', handler: 'nonexistent',
307
+ summary: 'Test', description: 'Test description.', project_id: 1, category: 'General', handler: 'nonexistent',
282
308
  });
283
309
 
284
310
  expect(result.isError).toBe(true);
@@ -297,7 +323,7 @@ describe('create_issue – handler username', () => {
297
323
  );
298
324
 
299
325
  await server.callTool('create_issue', {
300
- summary: 'Test', project_id: 1, category: 'General', handler_id: 99, handler: 'dom',
326
+ summary: 'Test', description: 'Test description.', project_id: 1, category: 'General', handler_id: 99, handler: 'dom',
301
327
  }, { validate: true });
302
328
 
303
329
  const body = JSON.parse(vi.mocked(fetch).mock.calls[0]![1]!.body as string) as { handler: { id: number } };