@dpesch/mantisbt-mcp-server 1.7.0 → 1.8.0
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 +20 -0
- package/README.de.md +6 -3
- package/README.md +6 -3
- package/dist/cache.js +3 -0
- package/dist/constants.js +5 -0
- package/dist/index.js +1 -1
- package/dist/resources/index.js +54 -6
- package/dist/tools/metadata.js +64 -5
- package/dist/tools/projects.js +51 -3
- package/docs/cookbook.de.md +158 -1
- package/docs/cookbook.md +158 -1
- package/docs/examples.de.md +6 -0
- package/docs/examples.md +6 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/tests/helpers/mock-server.ts +43 -14
- package/tests/resources/resources.test.ts +117 -0
- package/tests/tools/metadata.test.ts +128 -2
- package/tests/tools/projects.test.ts +153 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## [1.8.0] – 2026-03-27
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- New MCP resource `mantis://projects/{id}`: combined project view with fields (`status`, `view_state`, `access_level`, `description`) plus users, versions, and categories — data that was previously only accessible via separate tool calls. Served from local cache; falls back to three parallel API calls when the cache is cold.
|
|
18
|
+
- New tool `find_project_member`: search users with access to a project by name, display name, or email. Case-insensitive substring matching; returns up to `limit` results (default 10, max 100). Served from local cache when fresh; falls back to a live API call otherwise.
|
|
19
|
+
- New tool `get_metadata_full`: returns the complete raw metadata cache (all project fields, full user/version/category lists, all tags) as minified JSON. Use when the compact summary from `get_metadata` is not enough.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- `get_metadata` now returns a compact summary (project count, tag count, per-project counts of users/versions/categories, cache timestamp, and remaining TTL) instead of the full raw cache dump. Reduces response size from hundreds of KB to a few hundred bytes. Use `list_projects` for the full project list, `get_project_users` / `get_project_versions` / `get_project_categories` for per-project data, and `list_tags` for tags.
|
|
23
|
+
- `list_projects` now applies the same normalization as `sync_metadata`: `custom_fields` and other undeclared API fields are stripped from project objects, keeping the response lean and consistent.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- `mantis://projects` resource response reduced from ~270 KB to ~6 KB (98% smaller): `custom_fields` are no longer included in project objects (they were passed through from the raw MantisBT API response despite not being part of the project schema), JSON output is now minified, and enum sub-objects (`status`, `view_state`, `access_level`) are stripped to only their declared fields. The `label` field on `status` and `view_state` is preserved for localized display name lookups.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
10
30
|
## [1.7.0] – 2026-03-26
|
|
11
31
|
|
|
12
32
|
### Added
|
package/README.de.md
CHANGED
|
@@ -133,10 +133,11 @@ npm run build
|
|
|
133
133
|
|
|
134
134
|
| Tool | Beschreibung |
|
|
135
135
|
|---|---|
|
|
136
|
-
| `list_projects` | Alle zugänglichen Projekte auflisten |
|
|
136
|
+
| `list_projects` | Alle zugänglichen Projekte auflisten; gibt normalisierte Projektdaten zurück (konsistent mit dem `sync_metadata`-Cache) |
|
|
137
137
|
| `get_project_versions` | Versionen eines Projekts abrufen; optionale Booleans `obsolete` und `inherit` für veraltete bzw. vom Elternprojekt geerbte Versionen |
|
|
138
138
|
| `get_project_categories` | Kategorien eines Projekts abrufen |
|
|
139
139
|
| `get_project_users` | Benutzer eines Projekts abrufen |
|
|
140
|
+
| `find_project_member` | Projektmitglieder nach Name, Realname oder E-Mail suchen (Teilstring-Suche, Groß-/Kleinschreibung ignoriert); optionale Parameter `query` und `limit` (Standard 10, max. 100); Cache-first |
|
|
140
141
|
|
|
141
142
|
### Semantische Suche *(optional)*
|
|
142
143
|
|
|
@@ -177,7 +178,8 @@ npm install sqlite-vec better-sqlite3
|
|
|
177
178
|
| Tool | Beschreibung |
|
|
178
179
|
|---|---|
|
|
179
180
|
| `get_issue_fields` | Alle gültigen Feldnamen für den `select`-Parameter von `list_issues` zurückgeben |
|
|
180
|
-
| `get_metadata` |
|
|
181
|
+
| `get_metadata` | Kompakte Metadaten-Zusammenfassung abrufen: Anzahl Projekte/Tags und Anzahl Benutzer/Versionen/Kategorien je Projekt; für vollständige Arrays `get_metadata_full` verwenden |
|
|
182
|
+
| `get_metadata_full` | Vollständigen rohen Metadaten-Cache als minifiziertes JSON zurückgeben (alle Projekte mit vollständigen Feldern, Benutzer/Versionen/Kategorien je Projekt, alle Tags) |
|
|
181
183
|
| `sync_metadata` | Metadaten-Cache neu befüllen |
|
|
182
184
|
| `list_filters` | Gespeicherte Filter auflisten |
|
|
183
185
|
| `get_current_user` | Eigenes Benutzerprofil abrufen |
|
|
@@ -194,7 +196,8 @@ MCP-Ressourcen sind URI-adressierbare, schreibgeschützte Daten, die Clients dir
|
|
|
194
196
|
| Ressource-URI | Beschreibung |
|
|
195
197
|
|---|---|
|
|
196
198
|
| `mantis://me` | Profil des authentifizierten API-Benutzers (Live-Abruf) |
|
|
197
|
-
| `mantis://projects` | Alle zugänglichen MantisBT-Projekte (Cache-basiert, Aktualisierung via `sync_metadata`) |
|
|
199
|
+
| `mantis://projects` | Alle zugänglichen MantisBT-Projekte als kompakte Liste (Cache-basiert, Aktualisierung via `sync_metadata`) |
|
|
200
|
+
| `mantis://projects/{id}` | Kombinierte Projektansicht: Projektfelder + Benutzer + Versionen + Kategorien in einem Aufruf; Cache-first, List-Support zur Aufzählung aller verfügbaren Projekt-URIs |
|
|
198
201
|
| `mantis://enums` | Gültige Werte für alle Issue-Enum-Felder: Severity, Priority, Status, Resolution, Reproducibility (Live-Abruf) |
|
|
199
202
|
|
|
200
203
|
## Verfügbare Prompts
|
package/README.md
CHANGED
|
@@ -133,10 +133,11 @@ npm run build
|
|
|
133
133
|
|
|
134
134
|
| Tool | Description |
|
|
135
135
|
|---|---|
|
|
136
|
-
| `list_projects` | List all accessible projects |
|
|
136
|
+
| `list_projects` | List all accessible projects; returns normalized project data (consistent with `sync_metadata` cache) |
|
|
137
137
|
| `get_project_versions` | Get versions of a project; optional `obsolete` and `inherit` booleans to include obsolete or parent-inherited versions |
|
|
138
138
|
| `get_project_categories` | Get categories of a project |
|
|
139
139
|
| `get_project_users` | Get users of a project |
|
|
140
|
+
| `find_project_member` | Search project members by name, real name, or email (case-insensitive substring match); optional `query` and `limit` (default 10, max 100); cache-first |
|
|
140
141
|
|
|
141
142
|
### Semantic search *(optional)*
|
|
142
143
|
|
|
@@ -177,7 +178,8 @@ npm install sqlite-vec better-sqlite3
|
|
|
177
178
|
| Tool | Description |
|
|
178
179
|
|---|---|
|
|
179
180
|
| `get_issue_fields` | Return all field names valid for the `select` parameter of `list_issues` |
|
|
180
|
-
| `get_metadata` | Retrieve
|
|
181
|
+
| `get_metadata` | Retrieve a compact metadata summary: project/tag counts and per-project user/version/category counts; use `get_metadata_full` for complete arrays |
|
|
182
|
+
| `get_metadata_full` | Return the full raw metadata cache as minified JSON (all projects with complete fields, users/versions/categories per project, all tags) |
|
|
181
183
|
| `sync_metadata` | Refresh the metadata cache |
|
|
182
184
|
| `list_filters` | List saved filters |
|
|
183
185
|
| `get_current_user` | Retrieve your own user profile |
|
|
@@ -194,7 +196,8 @@ MCP Resources are URI-addressable, read-only data that clients can fetch directl
|
|
|
194
196
|
| Resource URI | Description |
|
|
195
197
|
|---|---|
|
|
196
198
|
| `mantis://me` | Profile of the authenticated API user (live fetch) |
|
|
197
|
-
| `mantis://projects` | All accessible MantisBT projects (cache-backed, refreshed via `sync_metadata`) |
|
|
199
|
+
| `mantis://projects` | All accessible MantisBT projects as a compact list (cache-backed, refreshed via `sync_metadata`) |
|
|
200
|
+
| `mantis://projects/{id}` | Combined project view: project fields + users + versions + categories in one call; cache-first, list-support for enumerating all available project URIs |
|
|
198
201
|
| `mantis://enums` | Valid values for all issue enum fields: severity, priority, status, resolution, reproducibility (live fetch) |
|
|
199
202
|
|
|
200
203
|
## Available prompts
|
package/dist/cache.js
CHANGED
|
@@ -77,6 +77,9 @@ export class MetadataCache {
|
|
|
77
77
|
return null;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
getRemainingTtlSeconds(timestampMs) {
|
|
81
|
+
return Math.round(this.ttlSeconds - (Date.now() - timestampMs) / 1000);
|
|
82
|
+
}
|
|
80
83
|
async saveIssueFields(fields) {
|
|
81
84
|
await mkdir(this.cacheDir, { recursive: true });
|
|
82
85
|
const file = { timestamp: Date.now(), fields };
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
+
// Category name normalization
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
/** Prefix MantisBT adds to category names inherited from [All Projects]. */
|
|
5
|
+
export const ALL_PROJECTS_PREFIX = '[All Projects] ';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
2
7
|
// Relationship type IDs
|
|
3
8
|
// ---------------------------------------------------------------------------
|
|
4
9
|
// Note: the MantisBT REST API only accepts numeric type IDs, not string names.
|
package/dist/index.js
CHANGED
|
@@ -55,7 +55,7 @@ async function createMcpServer() {
|
|
|
55
55
|
registerFileTools(server, client, startupConfig.uploadDir);
|
|
56
56
|
registerRelationshipTools(server, client);
|
|
57
57
|
registerMonitorTools(server, client);
|
|
58
|
-
registerProjectTools(server, client);
|
|
58
|
+
registerProjectTools(server, client, cache);
|
|
59
59
|
registerUserTools(server, client);
|
|
60
60
|
registerFilterTools(server, client);
|
|
61
61
|
registerConfigTools(server, client, cache);
|
package/dist/resources/index.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
1
2
|
import { fetchIssueEnums } from '../tools/config.js';
|
|
3
|
+
import { normalizeProject } from '../tools/metadata.js';
|
|
4
|
+
import { ALL_PROJECTS_PREFIX } from '../constants.js';
|
|
2
5
|
function jsonResource(uri, data) {
|
|
3
6
|
return {
|
|
4
|
-
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(data
|
|
7
|
+
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(data) }],
|
|
5
8
|
};
|
|
6
9
|
}
|
|
7
10
|
export function registerResources(server, client, cache) {
|
|
11
|
+
async function loadProjects() {
|
|
12
|
+
const cached = await cache.loadIfValid();
|
|
13
|
+
return cached?.projects
|
|
14
|
+
?? (await client.get('projects')).projects?.map(normalizeProject)
|
|
15
|
+
?? [];
|
|
16
|
+
}
|
|
8
17
|
server.registerResource('current-user', 'mantis://me', {
|
|
9
18
|
title: 'Current User',
|
|
10
19
|
description: 'Profile of the user associated with the configured API key.',
|
|
@@ -14,12 +23,51 @@ export function registerResources(server, client, cache) {
|
|
|
14
23
|
title: 'Projects',
|
|
15
24
|
description: 'All MantisBT projects accessible to the current API user. Served from local cache when fresh; falls back to live fetch. Refresh via the sync_metadata tool.',
|
|
16
25
|
mimeType: 'application/json',
|
|
17
|
-
}, async (uri) =>
|
|
26
|
+
}, async (uri) => jsonResource(uri, await loadProjects()));
|
|
27
|
+
server.registerResource('project-detail', new ResourceTemplate('mantis://projects/{id}', {
|
|
28
|
+
list: async () => ({
|
|
29
|
+
resources: (await loadProjects()).map((p) => ({
|
|
30
|
+
uri: `mantis://projects/${p.id}`,
|
|
31
|
+
name: p.name,
|
|
32
|
+
})),
|
|
33
|
+
}),
|
|
34
|
+
}), {
|
|
35
|
+
title: 'Project Detail',
|
|
36
|
+
description: 'Combined project view: fields (status, view_state, access_level, description) plus all associated users, versions, and categories. Served from local cache when fresh; falls back to live API fetch. Refresh via the sync_metadata tool.',
|
|
37
|
+
mimeType: 'application/json',
|
|
38
|
+
}, async (uri, { id }) => {
|
|
39
|
+
const numId = Number(id);
|
|
18
40
|
const cached = await cache.loadIfValid();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
let project;
|
|
42
|
+
let users;
|
|
43
|
+
let versions;
|
|
44
|
+
let categories;
|
|
45
|
+
if (cached) {
|
|
46
|
+
project = cached.projects.find((p) => p.id === numId);
|
|
47
|
+
const meta = cached.byProject[numId];
|
|
48
|
+
users = meta?.users ?? [];
|
|
49
|
+
versions = meta?.versions ?? [];
|
|
50
|
+
categories = meta?.categories ?? [];
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const [projectResult, usersResult, versionsResult] = await Promise.all([
|
|
54
|
+
client.get(`projects/${numId}`),
|
|
55
|
+
client.get(`projects/${numId}/users`),
|
|
56
|
+
client.get(`projects/${numId}/versions`, { obsolete: 1, inherit: 1 }),
|
|
57
|
+
]);
|
|
58
|
+
const raw = projectResult.projects?.[0];
|
|
59
|
+
project = raw ? normalizeProject(raw) : undefined;
|
|
60
|
+
users = usersResult.users ?? [];
|
|
61
|
+
versions = versionsResult.versions ?? [];
|
|
62
|
+
categories = (raw?.categories ?? []).map((c) => ({
|
|
63
|
+
...c,
|
|
64
|
+
name: c.name.startsWith(ALL_PROJECTS_PREFIX) ? c.name.slice(ALL_PROJECTS_PREFIX.length) : c.name,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
if (!project) {
|
|
68
|
+
throw new Error(`Project ${numId} not found`);
|
|
69
|
+
}
|
|
70
|
+
return jsonResource(uri, { ...project, users, versions, categories });
|
|
23
71
|
});
|
|
24
72
|
server.registerResource('issue-enums', 'mantis://enums', {
|
|
25
73
|
title: 'Issue Enums',
|
package/dist/tools/metadata.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getVersionHint } from '../version-hint.js';
|
|
3
|
+
import { ALL_PROJECTS_PREFIX } from '../constants.js';
|
|
3
4
|
// Fields MantisBT strips from issue responses when the array is empty.
|
|
4
5
|
// They are always valid 'select' values even if absent in a sample issue.
|
|
5
6
|
const EMPTY_STRIPPED_FIELDS = [
|
|
@@ -83,10 +84,27 @@ async function collectTagsFromIssues(client, projects) {
|
|
|
83
84
|
}
|
|
84
85
|
return Array.from(tagMap.values()).sort((a, b) => a.id - b.id);
|
|
85
86
|
}
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
+
function normalizeIdName(o) {
|
|
89
|
+
return { id: o.id, name: o.name, ...(o.label !== undefined && { label: o.label }) };
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
export function normalizeProject(p) {
|
|
93
|
+
return {
|
|
94
|
+
id: p.id,
|
|
95
|
+
name: p.name,
|
|
96
|
+
...(p.description !== undefined && { description: p.description }),
|
|
97
|
+
...(p.status !== undefined && { status: normalizeIdName(p.status) }),
|
|
98
|
+
...(p.enabled !== undefined && { enabled: p.enabled }),
|
|
99
|
+
...(p.view_state !== undefined && { view_state: normalizeIdName(p.view_state) }),
|
|
100
|
+
...(p.access_level !== undefined && { access_level: { id: p.access_level.id, label: p.access_level.label } }),
|
|
101
|
+
...(p.subprojects !== undefined && { subprojects: p.subprojects.map(normalizeProject) }),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
86
104
|
async function fetchAndCacheMetadata(client, cache) {
|
|
87
105
|
// Fetch all projects
|
|
88
106
|
const projectResult = await client.get('projects');
|
|
89
|
-
const projects = projectResult.projects ?? [];
|
|
107
|
+
const projects = (projectResult.projects ?? []).map(normalizeProject);
|
|
90
108
|
const byProject = {};
|
|
91
109
|
// For each project, fetch users, versions, categories in parallel
|
|
92
110
|
await Promise.all(projects.map(async (project) => {
|
|
@@ -102,7 +120,6 @@ async function fetchAndCacheMetadata(client, cache) {
|
|
|
102
120
|
const versions = versionsResult.status === 'fulfilled'
|
|
103
121
|
? (versionsResult.value.versions ?? [])
|
|
104
122
|
: [];
|
|
105
|
-
const ALL_PROJECTS_PREFIX = '[All Projects] ';
|
|
106
123
|
const rawCategories = projectDetailResult.status === 'fulfilled'
|
|
107
124
|
? (projectDetailResult.value.projects?.[0]?.categories ?? [])
|
|
108
125
|
: [];
|
|
@@ -177,10 +194,52 @@ Use this tool to refresh stale data.`,
|
|
|
177
194
|
// ---------------------------------------------------------------------------
|
|
178
195
|
server.registerTool('get_metadata', {
|
|
179
196
|
title: 'Get Cached Metadata',
|
|
180
|
-
description: `Return cached MantisBT metadata
|
|
197
|
+
description: `Return a compact summary of cached MantisBT metadata: project count, tag count, and per-project counts of users, versions, and categories.
|
|
198
|
+
|
|
199
|
+
If the cache does not exist or has expired (default TTL: 24 hours), it will automatically sync first.
|
|
200
|
+
Use sync_metadata to force a refresh. For full lists use: list_projects (projects), get_project_users / get_project_versions / get_project_categories (per-project data), list_tags (tags).`,
|
|
201
|
+
inputSchema: z.object({}),
|
|
202
|
+
annotations: {
|
|
203
|
+
readOnlyHint: true,
|
|
204
|
+
destructiveHint: false,
|
|
205
|
+
idempotentHint: true,
|
|
206
|
+
},
|
|
207
|
+
}, async () => {
|
|
208
|
+
try {
|
|
209
|
+
const data = await cache.loadIfValid() ?? await fetchAndCacheMetadata(client, cache);
|
|
210
|
+
const summary = {
|
|
211
|
+
projects: data.projects.length,
|
|
212
|
+
tags: data.tags.length,
|
|
213
|
+
byProject: Object.fromEntries(data.projects.map((p) => {
|
|
214
|
+
const meta = data.byProject[p.id];
|
|
215
|
+
return [String(p.id), {
|
|
216
|
+
name: p.name,
|
|
217
|
+
users: meta?.users.length ?? 0,
|
|
218
|
+
versions: meta?.versions.length ?? 0,
|
|
219
|
+
categories: meta?.categories.length ?? 0,
|
|
220
|
+
}];
|
|
221
|
+
})),
|
|
222
|
+
cached_at: new Date(data.timestamp).toISOString(),
|
|
223
|
+
ttl_seconds: cache.getRemainingTtlSeconds(data.timestamp),
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
231
|
+
return { content: [{ type: 'text', text: errorText(msg) }], isError: true };
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// get_metadata_full
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
server.registerTool('get_metadata_full', {
|
|
238
|
+
title: 'Get Full Cached Metadata',
|
|
239
|
+
description: `Return the complete raw MantisBT metadata cache: all projects with full fields, and per-project lists of users, versions, categories, plus all tags.
|
|
181
240
|
|
|
182
241
|
If the cache does not exist or has expired (default TTL: 24 hours), it will automatically sync first.
|
|
183
|
-
Use sync_metadata to force a refresh.`,
|
|
242
|
+
Use sync_metadata to force a refresh. For a lightweight overview use get_metadata instead.`,
|
|
184
243
|
inputSchema: z.object({}),
|
|
185
244
|
annotations: {
|
|
186
245
|
readOnlyHint: true,
|
|
@@ -191,7 +250,7 @@ Use sync_metadata to force a refresh.`,
|
|
|
191
250
|
try {
|
|
192
251
|
const data = await cache.loadIfValid() ?? await fetchAndCacheMetadata(client, cache);
|
|
193
252
|
return {
|
|
194
|
-
content: [{ type: 'text', text: JSON.stringify(data
|
|
253
|
+
content: [{ type: 'text', text: JSON.stringify(data) }],
|
|
195
254
|
};
|
|
196
255
|
}
|
|
197
256
|
catch (error) {
|
package/dist/tools/projects.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getVersionHint } from '../version-hint.js';
|
|
3
|
+
import { ALL_PROJECTS_PREFIX } from '../constants.js';
|
|
4
|
+
import { normalizeProject } from './metadata.js';
|
|
3
5
|
function errorText(msg) {
|
|
4
6
|
const vh = getVersionHint();
|
|
5
7
|
vh?.triggerLatestVersionFetch();
|
|
6
8
|
const hint = vh?.getUpdateHint();
|
|
7
9
|
return hint ? `Error: ${msg}\n\n${hint}` : `Error: ${msg}`;
|
|
8
10
|
}
|
|
9
|
-
|
|
10
|
-
export function registerProjectTools(server, client) {
|
|
11
|
+
export function registerProjectTools(server, client, cache) {
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
// list_projects
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
@@ -23,8 +24,9 @@ export function registerProjectTools(server, client) {
|
|
|
23
24
|
}, async () => {
|
|
24
25
|
try {
|
|
25
26
|
const result = await client.get('projects');
|
|
27
|
+
const projects = (result.projects ?? []).map(normalizeProject);
|
|
26
28
|
return {
|
|
27
|
-
content: [{ type: 'text', text: JSON.stringify(
|
|
29
|
+
content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }],
|
|
28
30
|
};
|
|
29
31
|
}
|
|
30
32
|
catch (error) {
|
|
@@ -131,4 +133,50 @@ This tool strips that prefix so the returned names can be used directly when cre
|
|
|
131
133
|
return { content: [{ type: 'text', text: errorText(msg) }], isError: true };
|
|
132
134
|
}
|
|
133
135
|
});
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// find_project_member
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
server.registerTool('find_project_member', {
|
|
140
|
+
title: 'Find Project Member',
|
|
141
|
+
description: `Search for users with access to a MantisBT project by name, display name, or email.
|
|
142
|
+
|
|
143
|
+
Returns up to \`limit\` matching users (default: 10, max: 100). Matching is case-insensitive substring search across \`name\`, \`real_name\`, and \`email\` fields. Omit \`query\` to list the first \`limit\` users.
|
|
144
|
+
|
|
145
|
+
Data is served from the local metadata cache when fresh; falls back to a live API call otherwise.`,
|
|
146
|
+
inputSchema: z.object({
|
|
147
|
+
project_id: z.coerce.number().int().positive().describe('Numeric project ID'),
|
|
148
|
+
query: z.string().optional().describe('Case-insensitive substring to match against name, real_name, or email'),
|
|
149
|
+
limit: z.coerce.number().int().min(1).max(100).default(10).describe('Maximum number of results to return (default: 10, max: 100)'),
|
|
150
|
+
}),
|
|
151
|
+
annotations: {
|
|
152
|
+
readOnlyHint: true,
|
|
153
|
+
destructiveHint: false,
|
|
154
|
+
idempotentHint: true,
|
|
155
|
+
},
|
|
156
|
+
}, async ({ project_id, query, limit }) => {
|
|
157
|
+
try {
|
|
158
|
+
let users;
|
|
159
|
+
const cached = cache ? await cache.loadIfValid() : null;
|
|
160
|
+
if (cached?.byProject[project_id]) {
|
|
161
|
+
users = cached.byProject[project_id].users;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const result = await client.get(`projects/${project_id}/users`);
|
|
165
|
+
users = result.users ?? [];
|
|
166
|
+
}
|
|
167
|
+
if (query) {
|
|
168
|
+
const q = query.toLowerCase();
|
|
169
|
+
users = users.filter((u) => u.name.toLowerCase().includes(q) ||
|
|
170
|
+
(u.real_name?.toLowerCase().includes(q) ?? false) ||
|
|
171
|
+
(u.email?.toLowerCase().includes(q) ?? false));
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
content: [{ type: 'text', text: JSON.stringify(users.slice(0, limit), null, 2) }],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
179
|
+
return { content: [{ type: 'text', text: errorText(msg) }], isError: true };
|
|
180
|
+
}
|
|
181
|
+
});
|
|
134
182
|
}
|
package/docs/cookbook.de.md
CHANGED
|
@@ -46,6 +46,10 @@ Tool-orientierte Rezepte für den MantisBT MCP Server — jedes Rezept zeigt gen
|
|
|
46
46
|
- [Suche mit Felderweiterung](#suche-mit-felderweiterung)
|
|
47
47
|
- [Projekte & Kategorien](#projekte--kategorien)
|
|
48
48
|
- [Projektkategorien auflisten](#projektkategorien-auflisten)
|
|
49
|
+
- [Projektmitglied suchen](#projektmitglied-suchen)
|
|
50
|
+
- [Metadaten](#metadaten)
|
|
51
|
+
- [Metadaten-Zusammenfassung abrufen](#metadaten-zusammenfassung-abrufen)
|
|
52
|
+
- [Vollständigen Metadaten-Cache abrufen](#vollständigen-metadaten-cache-abrufen)
|
|
49
53
|
- [Version & Diagnose](#version--diagnose)
|
|
50
54
|
- [MCP-Server-Version abrufen](#mcp-server-version-abrufen)
|
|
51
55
|
- [MantisBT-Version abrufen](#mantisbt-version-abrufen)
|
|
@@ -53,6 +57,7 @@ Tool-orientierte Rezepte für den MantisBT MCP Server — jedes Rezept zeigt gen
|
|
|
53
57
|
- [Ressourcen](#ressourcen)
|
|
54
58
|
- [Eigenes Benutzerprofil lesen](#eigenes-benutzerprofil-lesen)
|
|
55
59
|
- [Alle Projekte lesen](#alle-projekte-lesen)
|
|
60
|
+
- [Einzelnes Projekt mit allen Details lesen](#einzelnes-projekt-mit-allen-details-lesen)
|
|
56
61
|
- [Issue-Enum-Werte lesen](#issue-enum-werte-lesen)
|
|
57
62
|
- [Prompts](#prompts)
|
|
58
63
|
- [Bug-Report erstellen](#bug-report-erstellen)
|
|
@@ -1079,7 +1084,7 @@ Entfernt einen Tag von einem Issue. Erfordert die numerische Tag-ID.
|
|
|
1079
1084
|
"Tag #14 successfully removed from issue #1042."
|
|
1080
1085
|
```
|
|
1081
1086
|
|
|
1082
|
-
> **Hinweis:** `detach_tag` erfordert eine numerische ID, keinen Tag-Namen. Es gibt keine Suche per Name — die ID muss immer zuerst über `get_issue` oder `
|
|
1087
|
+
> **Hinweis:** `detach_tag` erfordert eine numerische ID, keinen Tag-Namen. Es gibt keine Suche per Name — die ID muss immer zuerst über `get_issue` oder `list_tags` abgerufen werden.
|
|
1083
1088
|
|
|
1084
1089
|
---
|
|
1085
1090
|
|
|
@@ -1338,6 +1343,123 @@ Gibt alle verfügbaren Kategorien eines MantisBT-Projekts zurück. Die zurückge
|
|
|
1338
1343
|
|
|
1339
1344
|
---
|
|
1340
1345
|
|
|
1346
|
+
### Projektmitglied suchen
|
|
1347
|
+
|
|
1348
|
+
Sucht Projektmitglieder nach Name, Realname oder E-Mail. Die Suche ist Groß-/Kleinschreibungsunabhängig und findet Teilstrings. Ergebnisse werden bevorzugt aus dem Metadaten-Cache geliefert; bei leerem Cache wird die live API verwendet.
|
|
1349
|
+
|
|
1350
|
+
**Tool:** `find_project_member`
|
|
1351
|
+
|
|
1352
|
+
**Parameter:**
|
|
1353
|
+
- `project_id` — numerische Projekt-ID
|
|
1354
|
+
- `query` — _(optional)_ Suchbegriff für `name`, `real_name` oder `email`
|
|
1355
|
+
- `limit` — _(optional)_ maximale Trefferanzahl, Standard 10, max. 100
|
|
1356
|
+
|
|
1357
|
+
**Request:**
|
|
1358
|
+
|
|
1359
|
+
```json
|
|
1360
|
+
{
|
|
1361
|
+
"project_id": 3,
|
|
1362
|
+
"query": "smith",
|
|
1363
|
+
"limit": 5
|
|
1364
|
+
}
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
**Response:**
|
|
1368
|
+
|
|
1369
|
+
```json
|
|
1370
|
+
[
|
|
1371
|
+
{ "id": 4, "name": "jsmith", "real_name": "John Smith", "email": "jsmith@example.com", "access_level": { "id": 55, "name": "developer" } },
|
|
1372
|
+
{ "id": 11, "name": "asmith", "real_name": "Alice Smith", "email": "asmith@example.com", "access_level": { "id": 40, "name": "reporter" } }
|
|
1373
|
+
]
|
|
1374
|
+
```
|
|
1375
|
+
|
|
1376
|
+
> **Tipp:** `query` weglassen, um alle Mitglieder des Projekts aufzulisten (bis zu `limit`).
|
|
1377
|
+
|
|
1378
|
+
---
|
|
1379
|
+
|
|
1380
|
+
## Metadaten
|
|
1381
|
+
|
|
1382
|
+
### Metadaten-Zusammenfassung abrufen
|
|
1383
|
+
|
|
1384
|
+
Gibt eine kompakte Übersicht aller gecachten Metadaten zurück: Gesamtzahl der Projekte und Tags sowie die Anzahl von Benutzern, Versionen und Kategorien je Projekt. Nützlich für einen schnellen Überblick, ohne große Arrays zu übertragen.
|
|
1385
|
+
|
|
1386
|
+
**Tool:** `get_metadata`
|
|
1387
|
+
|
|
1388
|
+
**Parameter:** _(keine)_
|
|
1389
|
+
|
|
1390
|
+
**Request:**
|
|
1391
|
+
|
|
1392
|
+
```json
|
|
1393
|
+
{}
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
**Response:**
|
|
1397
|
+
|
|
1398
|
+
```json
|
|
1399
|
+
{
|
|
1400
|
+
"projects": 24,
|
|
1401
|
+
"tags": 15,
|
|
1402
|
+
"byProject": {
|
|
1403
|
+
"3": { "name": "Webshop", "users": 8, "versions": 12, "categories": 4 },
|
|
1404
|
+
"5": { "name": "Backend API", "users": 5, "versions": 7, "categories": 3 }
|
|
1405
|
+
},
|
|
1406
|
+
"cached_at": "2026-03-27T09:00:00.000Z",
|
|
1407
|
+
"ttl_seconds": 82800
|
|
1408
|
+
}
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
> **Hinweis:** Für vollständige Listen `list_projects`, `get_project_users`, `get_project_versions`, `get_project_categories`, `list_tags` oder `get_metadata_full` verwenden.
|
|
1412
|
+
|
|
1413
|
+
---
|
|
1414
|
+
|
|
1415
|
+
### Vollständigen Metadaten-Cache abrufen
|
|
1416
|
+
|
|
1417
|
+
Gibt den vollständigen rohen Metadaten-Cache als minifiziertes JSON zurück. Enthält alle Projekte mit vollständigen Feldern sowie Benutzer, Versionen und Kategorien je Projekt und alle Tags. Geeignet, wenn alle Daten in einem einzigen Aufruf benötigt werden.
|
|
1418
|
+
|
|
1419
|
+
**Tool:** `get_metadata_full`
|
|
1420
|
+
|
|
1421
|
+
**Parameter:** _(keine)_
|
|
1422
|
+
|
|
1423
|
+
**Request:**
|
|
1424
|
+
|
|
1425
|
+
```json
|
|
1426
|
+
{}
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
**Response:**
|
|
1430
|
+
|
|
1431
|
+
```json
|
|
1432
|
+
{
|
|
1433
|
+
"projects": [
|
|
1434
|
+
{
|
|
1435
|
+
"id": 3,
|
|
1436
|
+
"name": "Webshop",
|
|
1437
|
+
"status": { "id": 10, "name": "development" },
|
|
1438
|
+
"enabled": true,
|
|
1439
|
+
"users": [
|
|
1440
|
+
{ "id": 4, "name": "jsmith", "real_name": "John Smith", "email": "jsmith@example.com", "access_level": { "id": 55, "name": "developer" } }
|
|
1441
|
+
],
|
|
1442
|
+
"versions": [
|
|
1443
|
+
{ "id": 21, "name": "2.4.0", "released": false, "obsolete": false }
|
|
1444
|
+
],
|
|
1445
|
+
"categories": [
|
|
1446
|
+
{ "id": 1, "name": "General" },
|
|
1447
|
+
{ "id": 2, "name": "UI" }
|
|
1448
|
+
]
|
|
1449
|
+
}
|
|
1450
|
+
],
|
|
1451
|
+
"tags": [
|
|
1452
|
+
{ "id": 1, "name": "regression" },
|
|
1453
|
+
{ "id": 2, "name": "hotfix" }
|
|
1454
|
+
],
|
|
1455
|
+
"cached_at": "2026-03-27T09:00:00.000Z"
|
|
1456
|
+
}
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
> **Tipp:** `get_metadata` liefert dieselben Daten als kompakte Zusammenfassung (nur Zählwerte). `get_metadata_full` verwenden, wenn die eigentlichen Arrays benötigt werden.
|
|
1460
|
+
|
|
1461
|
+
---
|
|
1462
|
+
|
|
1341
1463
|
## Version & Diagnose
|
|
1342
1464
|
|
|
1343
1465
|
### MCP-Server-Version abrufen
|
|
@@ -1476,6 +1598,41 @@ Gibt alle MantisBT-Projekte zurück, auf die der konfigurierte API-Key Zugriff h
|
|
|
1476
1598
|
|
|
1477
1599
|
---
|
|
1478
1600
|
|
|
1601
|
+
### Einzelnes Projekt mit allen Details lesen
|
|
1602
|
+
|
|
1603
|
+
Gibt eine kombinierte Ansicht eines einzelnen Projekts zurück: Projektfelder sowie alle Mitglieder, Versionen und Kategorien in einem Aufruf. Cache-first (MetadataCache); bei leerem Cache werden drei parallele API-Aufrufe durchgeführt. Clients mit Resource-List-Unterstützung können alle verfügbaren Projekt-URIs aufzählen.
|
|
1604
|
+
|
|
1605
|
+
**Ressource-URI:** `mantis://projects/{id}` (`{id}` durch die numerische Projekt-ID ersetzen)
|
|
1606
|
+
|
|
1607
|
+
**Abrufverhalten:** Cache-first (MetadataCache, Standard-TTL 24 h). Fällt auf Live-API-Aufrufe zurück, wenn der Cache leer ist.
|
|
1608
|
+
|
|
1609
|
+
**Beispiel-URI:** `mantis://projects/42`
|
|
1610
|
+
|
|
1611
|
+
**Response:**
|
|
1612
|
+
|
|
1613
|
+
```json
|
|
1614
|
+
{
|
|
1615
|
+
"id": 3,
|
|
1616
|
+
"name": "Webshop",
|
|
1617
|
+
"status": { "id": 10, "name": "development" },
|
|
1618
|
+
"enabled": true,
|
|
1619
|
+
"users": [
|
|
1620
|
+
{ "id": 4, "name": "jsmith", "real_name": "John Smith", "email": "jsmith@example.com", "access_level": { "id": 55, "name": "developer" } }
|
|
1621
|
+
],
|
|
1622
|
+
"versions": [
|
|
1623
|
+
{ "id": 21, "name": "2.4.0", "released": false, "obsolete": false }
|
|
1624
|
+
],
|
|
1625
|
+
"categories": [
|
|
1626
|
+
{ "id": 1, "name": "General" },
|
|
1627
|
+
{ "id": 2, "name": "UI" }
|
|
1628
|
+
]
|
|
1629
|
+
}
|
|
1630
|
+
```
|
|
1631
|
+
|
|
1632
|
+
> **Tipp:** `mantis://projects` für eine kompakte Liste aller Projekte verwenden, anschließend Detaildaten einzelner Projekte über `mantis://projects/{id}` abrufen.
|
|
1633
|
+
|
|
1634
|
+
---
|
|
1635
|
+
|
|
1479
1636
|
### Issue-Enum-Werte lesen
|
|
1480
1637
|
|
|
1481
1638
|
Gibt gültige ID/Name-Paare für alle Issue-Enum-Felder zurück (Severity, Priority, Status, Resolution, Reproducibility). Vor `create_issue` oder `update_issue` verwenden, um kanonische englische Namen nachzuschlagen.
|