@dpesch/mantisbt-mcp-server 1.16.0 → 1.17.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 +12 -0
- package/README.de.md +5 -3
- package/README.md +5 -3
- package/dist/search/highlight.js +1 -1
- package/dist/search/lexical.js +72 -0
- package/dist/search/store.js +10 -2
- package/dist/search/tools.js +2 -0
- package/docs/cookbook.de.md +33 -2
- package/docs/cookbook.md +33 -2
- package/docs/examples.de.md +5 -1
- package/docs/examples.md +5 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,18 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [1.17.0] – 2026-11-03
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Search benchmark: an exact-first measure for identifier queries (do all issues that contain the identifier lead the top 10?), a new `compound` query type for identifiers such as `CVE-2026-1234`, and fixed cases from `BENCHMARK_EXACT` that print their ranking.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Semantic search: `search_issues` lists every issue that contains an identifier from the query as a whole — an order number, a CVE, a version, a code name such as `MANTIS_SEARCH_DIR` or `getEmbeddingSignature` — before all issues that do not. The combined ranking found such issues, but put issues in between that were only close by meaning, often short ones (#8545). A query for `CVE-2026-1234` no longer counts an issue with another CVE of 2026 as a near match, and `1.15` does not match `1.15.2`. Identifiers are recognised by their form (underscore, inner capital, letters with digits, five or more digits, or words joined by `-` `.` `:` `/` with a digit), so plain words and short numbers such as "Android 15" keep the combined ranking; an identifier found in more than 20 issues does as well. Measured on 7,742 issues: for a rare identifier, all issues containing it now lead the top 10 in 97 % of the queries instead of 72 %; for a compound identifier such as `7.0.0-1014-azure` in 98 % instead of 11 %, and such a query finds its issue among the top 10 in 98 % instead of 64 %. Titles, sentences and paraphrases stay level or gain slightly. No index rebuild is needed.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
14
26
|
## [1.16.0] – 2026-11-02
|
|
15
27
|
|
|
16
28
|
### Added
|
package/README.de.md
CHANGED
|
@@ -220,13 +220,13 @@ Die semantische Suche versteht die *Bedeutung* einer Anfrage, nicht nur ihre Sti
|
|
|
220
220
|
- *„Performance-Probleme auf der Checkout-Seite"* – liefert verwandte Meldungen unabhängig von der verwendeten Terminologie
|
|
221
221
|
- *„doppelte Einträge in der Rechnungsliste"* – erkennt auch Issues, die als „zweifach angezeigt", „dupliziert" o.ä. beschrieben sind
|
|
222
222
|
|
|
223
|
-
Das Embedding-Modell (~80 MB) läuft vollständig **offline** – kein OpenAI-Key, keine externe API. Es wird beim ersten Start einmalig heruntergeladen und lokal gecacht. Issues werden bei jedem Serverstart inkrementell indexiert (nur neue und geänderte Issues werden neu verarbeitet). Starten mehrere Client-Sitzungen gleichzeitig einen Server, synchronisiert nur einer davon den gemeinsamen Index. Das Modell wird bei der ersten Nutzung geladen und nach 10 Minuten ohne Einbetten wieder entladen, damit ruhende Sitzungen seinen Speicher (~0,9 GB pro Serverprozess) nicht dauerhaft belegen. Lange Beschreibungen werden in bis zu 8 Abschnitte geteilt, damit auch Details weiter unten gefunden werden; der Index enthält dadurch etwa 1,7 Vektoren pro Issue (rund 115 MB bei 7.700 Issues), die jeder Serverprozess mit aktivierter Suche im Speicher hält. Die Rangfolge berücksichtigt zusätzlich exakte Stichworttreffer, sodass eine Suche nach einem Bezeichner, Fehlercode oder einer Versionsnummer die Issues findet, die ihn enthalten.
|
|
223
|
+
Das Embedding-Modell (~80 MB) läuft vollständig **offline** – kein OpenAI-Key, keine externe API. Es wird beim ersten Start einmalig heruntergeladen und lokal gecacht. Issues werden bei jedem Serverstart inkrementell indexiert (nur neue und geänderte Issues werden neu verarbeitet). Starten mehrere Client-Sitzungen gleichzeitig einen Server, synchronisiert nur einer davon den gemeinsamen Index. Das Modell wird bei der ersten Nutzung geladen und nach 10 Minuten ohne Einbetten wieder entladen, damit ruhende Sitzungen seinen Speicher (~0,9 GB pro Serverprozess) nicht dauerhaft belegen. Lange Beschreibungen werden in bis zu 8 Abschnitte geteilt, damit auch Details weiter unten gefunden werden; der Index enthält dadurch etwa 1,7 Vektoren pro Issue (rund 115 MB bei 7.700 Issues), die jeder Serverprozess mit aktivierter Suche im Speicher hält. Die Rangfolge berücksichtigt zusätzlich exakte Stichworttreffer, sodass eine Suche nach einem Bezeichner, Fehlercode oder einer Versionsnummer die Issues findet, die ihn enthalten – und alle Issues, die eine solche Kennung vollständig enthalten, vor allen anderen auflistet, sofern die Kennung nicht in mehr als 20 Issues vorkommt.
|
|
224
224
|
|
|
225
225
|
Aktivierung mit `MANTIS_SEARCH_ENABLED=true`.
|
|
226
226
|
|
|
227
227
|
| Tool | Beschreibung |
|
|
228
228
|
|---|---|
|
|
229
|
-
| `search_issues` | Natürlichsprachige Suche über alle indizierten Issues – kombiniert semantische Ähnlichkeit mit Stichwortsuche (BM25), damit auch exakte Begriffe wie Bezeichner oder Fehlercodes gefunden werden; liefert Top-N-Ergebnisse mit der Cosine-Similarity als `score`, sortiert nach der kombinierten Rangfolge; optionales `select` (kommagetrennte Feldnamen) reichert jedes Ergebnis mit den angeforderten Issue-Feldern an; optionales `highlight` (boolean, Standard `false`) fügt je Ergebnis ein `highlights`-Feld mit keyword-basierten Ausschnitten aus `summary` und `description` hinzu (Treffer werden in `**fett**` dargestellt) |
|
|
229
|
+
| `search_issues` | Natürlichsprachige Suche über alle indizierten Issues – kombiniert semantische Ähnlichkeit mit Stichwortsuche (BM25), damit auch exakte Begriffe wie Bezeichner oder Fehlercodes gefunden werden; Issues, die eine Kennung aus der Anfrage enthalten (Auftragsnummer, CVE, Version, Code-Bezeichner), stehen vorn; liefert Top-N-Ergebnisse mit der Cosine-Similarity als `score`, sortiert nach der kombinierten Rangfolge; optionales `select` (kommagetrennte Feldnamen) reichert jedes Ergebnis mit den angeforderten Issue-Feldern an; optionales `highlight` (boolean, Standard `false`) fügt je Ergebnis ein `highlights`-Feld mit keyword-basierten Ausschnitten aus `summary` und `description` hinzu (Treffer werden in `**fett**` dargestellt) |
|
|
230
230
|
| `rebuild_search_index` | Suchindex aufbauen oder aktualisieren; `full: true` löscht und baut ihn vollständig neu |
|
|
231
231
|
| `get_search_index_status` | **Veraltet:** stattdessen `get_metadata` (Feld `search_index`) verwenden – wird in 2.0.0 entfernt |
|
|
232
232
|
|
|
@@ -341,9 +341,11 @@ npm test # Tests ausführen (vitest)
|
|
|
341
341
|
npm run test:watch # Tests im Watch-Modus
|
|
342
342
|
npm run test:coverage # Coverage-Report
|
|
343
343
|
npm run benchmark # Token-Benchmark gegen eine Live-MantisBT-Instanz (siehe Token-Verbrauch oben)
|
|
344
|
-
npm run benchmark:search # Such-Trefferquote auf einem bestehenden Index (MANTIS_SEARCH_DIR), Vektor vs. hybrid
|
|
344
|
+
npm run benchmark:search # Such-Trefferquote und Exakt-Treffer-Reihenfolge auf einem bestehenden Index (MANTIS_SEARCH_DIR), Vektor vs. hybrid
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
+
`benchmark:search` liest optional handgeschriebene Umschreibungen (`BENCHMARK_PARAPHRASES`) und feste Fälle mit erwarteter Rangfolge (`BENCHMARK_EXACT`); die Dateiformate stehen am Anfang von `scripts/benchmark-search.ts`.
|
|
348
|
+
|
|
347
349
|
## Lizenz
|
|
348
350
|
|
|
349
351
|
MIT – siehe [LICENSE](LICENSE)
|
package/README.md
CHANGED
|
@@ -220,13 +220,13 @@ Semantic search understands the *meaning* behind a query, not just its keywords.
|
|
|
220
220
|
- *"performance problems on the checkout page"* — surfaces related reports regardless of the exact terminology used
|
|
221
221
|
- *"duplicate entries in the invoice list"* — catches issues described as "shown twice", "double records", etc.
|
|
222
222
|
|
|
223
|
-
The embedding model (~80 MB) runs entirely **offline** — no OpenAI key, no external API. It is downloaded once on first start and cached locally. Issues are indexed incrementally on every server start (only new and updated issues are re-indexed). When several client sessions start a server at the same time, only one of them syncs the shared index. The model is loaded on first use and unloaded again after 10 minutes without embedding, so idle sessions do not hold on to its memory (~0.9 GB per server process). Long descriptions are split into up to 8 chunks so that details further down are found as well; the index then holds about 1.7 vectors per issue (around 115 MB for 7,700 issues), which every server process with search enabled keeps in memory. The ranking also takes exact keyword matches into account, so a query for an identifier, error code or version number finds the issues that contain it.
|
|
223
|
+
The embedding model (~80 MB) runs entirely **offline** — no OpenAI key, no external API. It is downloaded once on first start and cached locally. Issues are indexed incrementally on every server start (only new and updated issues are re-indexed). When several client sessions start a server at the same time, only one of them syncs the shared index. The model is loaded on first use and unloaded again after 10 minutes without embedding, so idle sessions do not hold on to its memory (~0.9 GB per server process). Long descriptions are split into up to 8 chunks so that details further down are found as well; the index then holds about 1.7 vectors per issue (around 115 MB for 7,700 issues), which every server process with search enabled keeps in memory. The ranking also takes exact keyword matches into account, so a query for an identifier, error code or version number finds the issues that contain it — and lists all issues containing such an identifier as a whole before any others, unless it occurs in more than 20 issues.
|
|
224
224
|
|
|
225
225
|
Activate with `MANTIS_SEARCH_ENABLED=true`.
|
|
226
226
|
|
|
227
227
|
| Tool | Description |
|
|
228
228
|
|---|---|
|
|
229
|
-
| `search_issues` | Natural language search over all indexed issues — combines semantic similarity with keyword matching (BM25), so exact terms such as identifiers or error codes are found as well; returns top-N results with the cosine similarity as `score`, ordered by the combined ranking; optional `select` (comma-separated field names) enriches each result with the requested issue fields; optional `highlight` (boolean, default `false`) adds a `highlights` field per result with keyword-matched excerpts from `summary` and `description` (matched terms shown in `**bold**`) |
|
|
229
|
+
| `search_issues` | Natural language search over all indexed issues — combines semantic similarity with keyword matching (BM25), so exact terms such as identifiers or error codes are found as well; issues containing an identifier from the query (order number, CVE, version, code name) come first; returns top-N results with the cosine similarity as `score`, ordered by the combined ranking; optional `select` (comma-separated field names) enriches each result with the requested issue fields; optional `highlight` (boolean, default `false`) adds a `highlights` field per result with keyword-matched excerpts from `summary` and `description` (matched terms shown in `**bold**`) |
|
|
230
230
|
| `rebuild_search_index` | Build or update the search index; `full: true` clears and rebuilds from scratch |
|
|
231
231
|
| `get_search_index_status` | **Deprecated:** use `get_metadata` (field `search_index`) instead — will be removed in 2.0.0 |
|
|
232
232
|
|
|
@@ -341,9 +341,11 @@ npm test # Run tests (vitest)
|
|
|
341
341
|
npm run test:watch # Run tests in watch mode
|
|
342
342
|
npm run test:coverage # Coverage report
|
|
343
343
|
npm run benchmark # Token benchmark against a live MantisBT (see Token usage above)
|
|
344
|
-
npm run benchmark:search # Search recall on an existing index (MANTIS_SEARCH_DIR), vectors vs. hybrid
|
|
344
|
+
npm run benchmark:search # Search recall and exact-first order on an existing index (MANTIS_SEARCH_DIR), vectors vs. hybrid
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
+
`benchmark:search` optionally reads hand-written paraphrases (`BENCHMARK_PARAPHRASES`) and fixed cases with an expected ranking (`BENCHMARK_EXACT`); the file formats are described at the top of `scripts/benchmark-search.ts`.
|
|
348
|
+
|
|
347
349
|
## License
|
|
348
350
|
|
|
349
351
|
MIT – see [LICENSE](LICENSE)
|
package/dist/search/highlight.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Note: highlights are keyword-based (lexical), not semantic.
|
|
5
5
|
* A result may have no highlighted terms even if it is semantically relevant.
|
|
6
6
|
*/
|
|
7
|
-
function escapeRegex(term) {
|
|
7
|
+
export function escapeRegex(term) {
|
|
8
8
|
return term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
9
9
|
}
|
|
10
10
|
function combinedPattern(terms, flags) {
|
package/dist/search/lexical.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// keyword ranking. Built in memory from the summaries and descriptions the
|
|
7
7
|
// vector index already stores — no extra API calls.
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
|
+
import { escapeRegex } from './highlight.js';
|
|
9
10
|
// Standard BM25 parameters.
|
|
10
11
|
const K1 = 1.2;
|
|
11
12
|
const B = 0.75;
|
|
@@ -15,7 +16,48 @@ const SUMMARY_WEIGHT = 2;
|
|
|
15
16
|
export function tokenize(text) {
|
|
16
17
|
return (text.toLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? []).filter(word => word.length >= 2);
|
|
17
18
|
}
|
|
19
|
+
// An identifier (#8545) found in more issues than this is no longer specific
|
|
20
|
+
// enough to put all of them first; the fused ranking decides instead. Keeps
|
|
21
|
+
// common tokens such as UTF-8 from taking over a search. The rare identifiers
|
|
22
|
+
// of the search benchmark occur in at most 8 issues, well below the cap.
|
|
23
|
+
const MAX_EXACT_MATCHES = 20;
|
|
24
|
+
// Words, or words joined by - . : / (CVE-2026-1234, 7.0.0-1014-azure).
|
|
25
|
+
const COMPOUND = /[\p{L}\p{N}_]+(?:[-.:/][\p{L}\p{N}_]+)*/gu;
|
|
26
|
+
function looksLikeIdentifier(word) {
|
|
27
|
+
return word.includes('_') // MANTIS_SEARCH_DIR, _resizeTimer
|
|
28
|
+
|| /\p{Ll}\p{Lu}/u.test(word) // ScrollViewHelper
|
|
29
|
+
|| (word.length >= 4 && /\p{L}/u.test(word) && /\p{N}/u.test(word)) // host01
|
|
30
|
+
|| /^\p{N}{5,}$/u.test(word); // 202699999
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Identifiers in a search query: order numbers, CVEs, versions, code names.
|
|
34
|
+
* Plain words, short numbers ("Android 15") and hyphenated words without
|
|
35
|
+
* digits ("Play-Store-Hinweis") are none.
|
|
36
|
+
*/
|
|
37
|
+
export function identifiers(query) {
|
|
38
|
+
const found = new Map();
|
|
39
|
+
for (const match of query.match(COMPOUND) ?? []) {
|
|
40
|
+
// A compound with a digit is one identifier; otherwise each word counts alone.
|
|
41
|
+
const candidates = /[-.:/]/.test(match) && /\p{N}/u.test(match)
|
|
42
|
+
? [match]
|
|
43
|
+
: match.split(/[-.:/]/).filter(looksLikeIdentifier);
|
|
44
|
+
for (const candidate of candidates) {
|
|
45
|
+
if (!found.has(candidate.toLowerCase()))
|
|
46
|
+
found.set(candidate.toLowerCase(), candidate);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return [...found.values()];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Matches the identifier as a whole: not inside a longer word, and not as the
|
|
53
|
+
* start or end of a longer number or version (1.15 is not in 1.15.2), while a
|
|
54
|
+
* word may continue with a hyphen (ScrollViewHelper in ScrollViewHelper-Bridge).
|
|
55
|
+
*/
|
|
56
|
+
export function wholeIdentifier(identifier) {
|
|
57
|
+
return new RegExp(`(?<![\\p{L}\\p{N}_])(?<!\\p{N}[-.:/])${escapeRegex(identifier)}(?![\\p{L}\\p{N}_])(?![-.:/]\\p{N})`, 'iu');
|
|
58
|
+
}
|
|
18
59
|
export class LexicalIndex {
|
|
60
|
+
documents;
|
|
19
61
|
// Postings in compressed sparse rows: the documents of term t are
|
|
20
62
|
// docs[offsets[t] .. offsets[t + 1]], with their term frequencies in tfs.
|
|
21
63
|
// Typed arrays keep 340,000 postings (7,700 issues) at about 7 MB instead of
|
|
@@ -28,6 +70,7 @@ export class LexicalIndex {
|
|
|
28
70
|
lengths;
|
|
29
71
|
avgLength;
|
|
30
72
|
constructor(documents) {
|
|
73
|
+
this.documents = documents;
|
|
31
74
|
this.ids = Uint32Array.from(documents, doc => doc.id);
|
|
32
75
|
this.lengths = new Uint32Array(documents.length);
|
|
33
76
|
const counts = documents.map((doc, index) => {
|
|
@@ -85,4 +128,33 @@ export class LexicalIndex {
|
|
|
85
128
|
}
|
|
86
129
|
return new Map([...byDoc].map(([doc, score]) => [this.ids[doc], score]));
|
|
87
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Per document id, how many identifiers of the query it contains as a whole
|
|
133
|
+
* (#8545); documents without one are absent. Candidates come from the
|
|
134
|
+
* postings of the identifier's rarest word, the text check decides.
|
|
135
|
+
*/
|
|
136
|
+
exactMatches(query) {
|
|
137
|
+
const counts = new Map();
|
|
138
|
+
const frequency = (term) => this.offsets[term + 1] - this.offsets[term];
|
|
139
|
+
for (const identifier of identifiers(query)) {
|
|
140
|
+
const terms = tokenize(identifier).map(word => this.termIds.get(word));
|
|
141
|
+
if (terms.length === 0 || terms.some(term => term === undefined))
|
|
142
|
+
continue;
|
|
143
|
+
const rarest = terms.reduce((a, b) => (frequency(a) <= frequency(b) ? a : b));
|
|
144
|
+
const pattern = wholeIdentifier(identifier);
|
|
145
|
+
const matches = [];
|
|
146
|
+
for (let at = this.offsets[rarest]; at < this.offsets[rarest + 1]; at++) {
|
|
147
|
+
const doc = this.documents[this.docs[at]];
|
|
148
|
+
if (pattern.test(doc.summary) || pattern.test(doc.description ?? ''))
|
|
149
|
+
matches.push(doc.id);
|
|
150
|
+
if (matches.length > MAX_EXACT_MATCHES)
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
if (matches.length > MAX_EXACT_MATCHES)
|
|
154
|
+
continue;
|
|
155
|
+
for (const id of matches)
|
|
156
|
+
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
157
|
+
}
|
|
158
|
+
return counts;
|
|
159
|
+
}
|
|
88
160
|
}
|
package/dist/search/store.js
CHANGED
|
@@ -107,11 +107,19 @@ export class VectraStore {
|
|
|
107
107
|
// Reciprocal Rank Fusion of the vector ranking and the keyword ranking.
|
|
108
108
|
this.lexical ??= new LexicalIndex([...this.items.values()].map(item => ({ id: item.id, ...item.metadata })));
|
|
109
109
|
const keywordRank = new Map([...this.lexical.scores(queryText)].sort((a, b) => b[1] - a[1]).map(([id], rank) => [id, rank]));
|
|
110
|
+
// Issues containing an identifier of the query as a whole come first
|
|
111
|
+
// (#8545): a search for an order number or a CVE lists all its issues
|
|
112
|
+
// before any that merely resemble the query.
|
|
113
|
+
const exact = this.lexical.exactMatches(queryText);
|
|
110
114
|
const fused = results.map((result, vectorRank) => {
|
|
111
115
|
const rank = keywordRank.get(result.id);
|
|
112
|
-
return {
|
|
116
|
+
return {
|
|
117
|
+
result,
|
|
118
|
+
exact: exact.get(result.id) ?? 0,
|
|
119
|
+
fused: 1 / (RRF_K + vectorRank) + (rank === undefined ? 0 : 1 / (RRF_K + rank)),
|
|
120
|
+
};
|
|
113
121
|
});
|
|
114
|
-
fused.sort((a, b) => b.fused - a.fused);
|
|
122
|
+
fused.sort((a, b) => b.exact - a.exact || b.fused - a.fused);
|
|
115
123
|
return fused.slice(0, topN).map(f => f.result);
|
|
116
124
|
}
|
|
117
125
|
async getItem(id) {
|
package/dist/search/tools.js
CHANGED
|
@@ -49,6 +49,8 @@ export function registerSearchTools(server, client, store, embedder, lockDir) {
|
|
|
49
49
|
'by combining semantic similarity with keyword matching, so exact terms such as identifiers, ' +
|
|
50
50
|
'error codes or version numbers are found as well. "score" is the semantic similarity; results ' +
|
|
51
51
|
'are ordered by the combined ranking, so a keyword match can precede a higher score. ' +
|
|
52
|
+
'Issues that contain an identifier from the query as a whole (order number, CVE, version, ' +
|
|
53
|
+
'code name such as MANTIS_SEARCH_DIR) are listed before all others, unless the identifier is common. ' +
|
|
52
54
|
'The search index must be populated first via rebuild_search_index.',
|
|
53
55
|
inputSchema: z.object({
|
|
54
56
|
query: z.string().describe('Natural language search query'),
|
package/docs/cookbook.de.md
CHANGED
|
@@ -1684,7 +1684,7 @@ Gibt die Anzahl der indizierten Issues, die Gesamtanzahl der Issues und den Zeit
|
|
|
1684
1684
|
|
|
1685
1685
|
### Nach Bedeutung suchen
|
|
1686
1686
|
|
|
1687
|
-
Findet Issues, die einem natürlichsprachigen Suchbegriff semantisch ähnlich sind, kombiniert mit einer Stichwortsuche, damit auch exakte Begriffe (Bezeichner, Fehlercodes, Versionsnummern) gefunden werden. Gibt Issue-IDs und Relevanz-Scores zurück. `score` ist die semantische Ähnlichkeit; die Reihenfolge folgt der kombinierten Rangfolge, ein Ergebnis mit exaktem Stichworttreffer kann also vor einem mit höherem `score` stehen.
|
|
1687
|
+
Findet Issues, die einem natürlichsprachigen Suchbegriff semantisch ähnlich sind, kombiniert mit einer Stichwortsuche, damit auch exakte Begriffe (Bezeichner, Fehlercodes, Versionsnummern) gefunden werden. Gibt Issue-IDs und Relevanz-Scores zurück. `score` ist die semantische Ähnlichkeit; die Reihenfolge folgt der kombinierten Rangfolge, ein Ergebnis mit exaktem Stichworttreffer kann also vor einem mit höherem `score` stehen. Issues, die eine Kennung aus der Anfrage vollständig enthalten – eine Auftragsnummer, eine CVE, eine Version, einen Code-Bezeichner wie `MANTIS_SEARCH_DIR` –, stehen vor allen anderen, es sei denn, die Kennung kommt in mehr als 20 Issues vor.
|
|
1688
1688
|
|
|
1689
1689
|
**Tool:** `search_issues`
|
|
1690
1690
|
|
|
@@ -1712,7 +1712,38 @@ Findet Issues, die einem natürlichsprachigen Suchbegriff semantisch ähnlich si
|
|
|
1712
1712
|
]
|
|
1713
1713
|
```
|
|
1714
1714
|
|
|
1715
|
-
> **Hinweis:** Die semantische Suche gibt die top-N ähnlichsten Issues zurück – vollständige Treffergarantie besteht nicht. Sie eignet sich nicht für Abfragen wie »alle Issues zu Thema X auflisten«.
|
|
1715
|
+
> **Hinweis:** Die semantische Suche gibt die top-N ähnlichsten Issues zurück – vollständige Treffergarantie besteht nicht. Sie eignet sich nicht für Abfragen wie »alle Issues zu Thema X auflisten«. Ausnahme ist die Suche nach einer seltenen Kennung: Die Issues, die sie enthalten, stehen vorn (siehe unten), mit ausreichend großem `top_n` werden also alle aufgelistet.
|
|
1716
|
+
|
|
1717
|
+
---
|
|
1718
|
+
|
|
1719
|
+
### Nach einer Kennung suchen
|
|
1720
|
+
|
|
1721
|
+
Eine Suche nach einer Auftragsnummer, einer CVE, einer Version oder einem Code-Bezeichner listet alle Issues, die sie vollständig enthalten, vor allen anderen Ergebnissen. Kennungen werden an ihrer Form erkannt: ein Unterstrich, ein Großbuchstabe mitten im Wort, Buchstaben mit Ziffern, fünf oder mehr Ziffern oder mit `-` `.` `:` `/` verbundene Teile mit einer Ziffer. `1.15` trifft nicht `1.15.2`, und `CVE-2026-1234` trifft nicht `CVE-2026-12345`. Gewöhnliche Wörter und kurze Zahlen behalten die kombinierte Rangfolge, ebenso eine Kennung, die in mehr als 20 Issues vorkommt.
|
|
1722
|
+
|
|
1723
|
+
**Tool:** `search_issues`
|
|
1724
|
+
|
|
1725
|
+
**Request:**
|
|
1726
|
+
|
|
1727
|
+
```json
|
|
1728
|
+
{
|
|
1729
|
+
"query": "CVE-2026-1234",
|
|
1730
|
+
"top_n": 5
|
|
1731
|
+
}
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
**Response:**
|
|
1735
|
+
|
|
1736
|
+
```json
|
|
1737
|
+
[
|
|
1738
|
+
{ "id": 2210, "score": 0.58, "view_url": "https://mantis.example.com/view.php?id=2210" },
|
|
1739
|
+
{ "id": 2211, "score": 0.47, "view_url": "https://mantis.example.com/view.php?id=2211" },
|
|
1740
|
+
{ "id": 2084, "score": 0.65, "view_url": "https://mantis.example.com/view.php?id=2084" },
|
|
1741
|
+
{ "id": 1977, "score": 0.63, "view_url": "https://mantis.example.com/view.php?id=1977" },
|
|
1742
|
+
{ "id": 2150, "score": 0.61, "view_url": "https://mantis.example.com/view.php?id=2150" }
|
|
1743
|
+
]
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
Die Issues 2210 und 2211 enthalten `CVE-2026-1234` und stehen vorn, obwohl Issue 2084 den höheren `score` hat.
|
|
1716
1747
|
|
|
1717
1748
|
---
|
|
1718
1749
|
|
package/docs/cookbook.md
CHANGED
|
@@ -1684,7 +1684,7 @@ Returns the number of indexed issues, total issue count, and the timestamp of th
|
|
|
1684
1684
|
|
|
1685
1685
|
### Search by meaning
|
|
1686
1686
|
|
|
1687
|
-
Finds issues semantically similar to a natural language query, combined with keyword matching so that exact terms (identifiers, error codes, version numbers) are found as well. Returns issue IDs and relevance scores. `score` is the semantic similarity; the order follows the combined ranking, so a result with an exact keyword match can come before one with a higher `score`.
|
|
1687
|
+
Finds issues semantically similar to a natural language query, combined with keyword matching so that exact terms (identifiers, error codes, version numbers) are found as well. Returns issue IDs and relevance scores. `score` is the semantic similarity; the order follows the combined ranking, so a result with an exact keyword match can come before one with a higher `score`. Issues that contain an identifier from the query as a whole — an order number, a CVE, a version, a code name such as `MANTIS_SEARCH_DIR` — are listed before all others, unless the identifier occurs in more than 20 issues.
|
|
1688
1688
|
|
|
1689
1689
|
**Tool:** `search_issues`
|
|
1690
1690
|
|
|
@@ -1712,7 +1712,38 @@ Finds issues semantically similar to a natural language query, combined with key
|
|
|
1712
1712
|
]
|
|
1713
1713
|
```
|
|
1714
1714
|
|
|
1715
|
-
> **Note:** Semantic search returns the top-N most similar issues — it does not guarantee exhaustive recall. It is not suitable for "find all issues about X" census queries.
|
|
1715
|
+
> **Note:** Semantic search returns the top-N most similar issues — it does not guarantee exhaustive recall. It is not suitable for "find all issues about X" census queries. The exception is a query for a rare identifier: the issues that contain it come first (see below), so with a large enough `top_n` they are all listed.
|
|
1716
|
+
|
|
1717
|
+
---
|
|
1718
|
+
|
|
1719
|
+
### Search for an identifier
|
|
1720
|
+
|
|
1721
|
+
A query for an order number, a CVE, a version or a code name lists every issue that contains it as a whole before all other results. Identifiers are recognised by their form: an underscore, a capital inside the word, letters with digits, five or more digits, or parts joined by `-` `.` `:` `/` with a digit. `1.15` does not match `1.15.2`, and `CVE-2026-1234` does not match `CVE-2026-12345`. Plain words and short numbers keep the combined ranking, as does an identifier that occurs in more than 20 issues.
|
|
1722
|
+
|
|
1723
|
+
**Tool:** `search_issues`
|
|
1724
|
+
|
|
1725
|
+
**Request:**
|
|
1726
|
+
|
|
1727
|
+
```json
|
|
1728
|
+
{
|
|
1729
|
+
"query": "CVE-2026-1234",
|
|
1730
|
+
"top_n": 5
|
|
1731
|
+
}
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
**Response:**
|
|
1735
|
+
|
|
1736
|
+
```json
|
|
1737
|
+
[
|
|
1738
|
+
{ "id": 2210, "score": 0.58, "view_url": "https://mantis.example.com/view.php?id=2210" },
|
|
1739
|
+
{ "id": 2211, "score": 0.47, "view_url": "https://mantis.example.com/view.php?id=2211" },
|
|
1740
|
+
{ "id": 2084, "score": 0.65, "view_url": "https://mantis.example.com/view.php?id=2084" },
|
|
1741
|
+
{ "id": 1977, "score": 0.63, "view_url": "https://mantis.example.com/view.php?id=1977" },
|
|
1742
|
+
{ "id": 2150, "score": 0.61, "view_url": "https://mantis.example.com/view.php?id=2150" }
|
|
1743
|
+
]
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
Issues 2210 and 2211 contain `CVE-2026-1234` and come first, although issue 2084 has the higher `score`.
|
|
1716
1747
|
|
|
1717
1748
|
---
|
|
1718
1749
|
|
package/docs/examples.de.md
CHANGED
|
@@ -201,10 +201,14 @@ Die semantische Suche versteht die *Bedeutung* deiner Frage – nicht nur einzel
|
|
|
201
201
|
|
|
202
202
|
### Exakte Begriffe
|
|
203
203
|
|
|
204
|
-
Die Suche berücksichtigt auch exakte Stichwörter, daher funktionieren Bezeichner, Fehlercodes und Versionsnummern ebenfalls als Suchanfrage.
|
|
204
|
+
Die Suche berücksichtigt auch exakte Stichwörter, daher funktionieren Bezeichner, Fehlercodes und Versionsnummern ebenfalls als Suchanfrage. Issues, die die Kennung enthalten, stehen vorn.
|
|
205
205
|
|
|
206
206
|
> »Welche Issues erwähnen `MANTIS_SEARCH_DIR`?«
|
|
207
207
|
|
|
208
|
+
> »Finde alle Issues zu `CVE-2026-1234`.«
|
|
209
|
+
|
|
210
|
+
Wie Kennungen erkannt werden: [Cookbook – Nach einer Kennung suchen](cookbook.de.md#nach-einer-kennung-suchen).
|
|
211
|
+
|
|
208
212
|
> »Finde Issues zum Fehler `ERR_CERT_DATE_INVALID`.«
|
|
209
213
|
|
|
210
214
|
---
|
package/docs/examples.md
CHANGED
|
@@ -201,10 +201,14 @@ Semantic search understands the *meaning* of your question — not just keywords
|
|
|
201
201
|
|
|
202
202
|
### Exact terms
|
|
203
203
|
|
|
204
|
-
The search also matches exact keywords, so identifiers, error codes and version numbers work as queries too.
|
|
204
|
+
The search also matches exact keywords, so identifiers, error codes and version numbers work as queries too. Issues that contain the identifier come first.
|
|
205
205
|
|
|
206
206
|
> "Which issues mention `MANTIS_SEARCH_DIR`?"
|
|
207
207
|
|
|
208
|
+
> "Find all issues about `CVE-2026-1234`."
|
|
209
|
+
|
|
210
|
+
How identifiers are recognised: [Cookbook — Search for an identifier](cookbook.md#search-for-an-identifier).
|
|
211
|
+
|
|
208
212
|
> "Find issues about the error `ERR_CERT_DATE_INVALID`."
|
|
209
213
|
|
|
210
214
|
---
|
package/package.json
CHANGED
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
|
+
"version": "1.17.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "@dpesch/mantisbt-mcp-server",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.17.0",
|
|
12
12
|
"runtimeHint": "npx",
|
|
13
13
|
"transport": {
|
|
14
14
|
"type": "stdio"
|