@dpesch/mantisbt-mcp-server 1.5.0 → 1.5.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 +14 -0
- package/README.de.md +1 -0
- package/README.md +1 -0
- package/dist/config.js +2 -0
- package/dist/index.js +4 -0
- package/dist/search/embedder.js +10 -3
- package/dist/search/index.js +1 -1
- package/package.json +1 -1
- package/tests/search/embedder.test.ts +81 -0
- package/.env.local +0 -3
- package/tests/fixtures/recorded/get_current_user.json +0 -108
- package/tests/fixtures/recorded/get_issue.json +0 -138
- package/tests/fixtures/recorded/get_issue_enums.json +0 -67
- package/tests/fixtures/recorded/get_issue_fields_sample.json +0 -138
- package/tests/fixtures/recorded/get_project_categories.json +0 -241
- package/tests/fixtures/recorded/get_project_versions.json +0 -28
- package/tests/fixtures/recorded/list_issues.json +0 -463
- package/tests/fixtures/recorded/list_projects.json +0 -10641
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.5.2] – 2026-03-17
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Semantic search: stdio server processes no longer accumulate as zombie processes after the Claude session ends. Previously, a background index sync kept the Node.js event loop alive indefinitely after stdin was closed, causing a new process to pile up on every session start. The server now exits immediately when stdin closes.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## [1.5.1] – 2026-03-17
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- Semantic search: ONNX thread pool now defaults to 1 thread (`intra_op_num_threads=1`) instead of auto-detecting all available CPU cores. On WSL and multi-core machines the unrestricted default caused CPU saturation (700%+ CPU, 12 GB VM) during the initial index build. The number of threads is configurable via the new `MANTIS_SEARCH_THREADS` environment variable (default: `1`). `inter_op_num_threads` is always kept at 1 because Transformer model graphs are sequential and inter-op parallelism provides no benefit.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
10
24
|
## [1.5.0] – 2026-03-17
|
|
11
25
|
|
|
12
26
|
### Added
|
package/README.de.md
CHANGED
|
@@ -73,6 +73,7 @@ npm run build
|
|
|
73
73
|
| `MANTIS_SEARCH_BACKEND` | – | `vectra` | Vektorspeicher: `vectra` (reines JS) oder `sqlite-vec` (manuelle Installation erforderlich) |
|
|
74
74
|
| `MANTIS_SEARCH_DIR` | – | `{MANTIS_CACHE_DIR}/search` | Verzeichnis für den Suchindex |
|
|
75
75
|
| `MANTIS_SEARCH_MODEL` | – | `Xenova/paraphrase-multilingual-MiniLM-L12-v2` | Embedding-Modell (wird beim ersten Start einmalig heruntergeladen, ~80 MB) |
|
|
76
|
+
| `MANTIS_SEARCH_THREADS` | – | `1` | Anzahl der ONNX-Intra-Op-Threads für das Embedding-Modell. Standard ist 1, um CPU-Sättigung auf Mehrkernsystemen und in WSL zu verhindern. Nur erhöhen, wenn die Indexierungsgeschwindigkeit kritisch ist und der Host ausschließlich für diese Last vorgesehen ist. |
|
|
76
77
|
|
|
77
78
|
### Config-Datei (Fallback)
|
|
78
79
|
|
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ npm run build
|
|
|
73
73
|
| `MANTIS_SEARCH_BACKEND` | – | `vectra` | Vector store backend: `vectra` (pure JS) or `sqlite-vec` (requires manual install) |
|
|
74
74
|
| `MANTIS_SEARCH_DIR` | – | `{MANTIS_CACHE_DIR}/search` | Directory for the search index |
|
|
75
75
|
| `MANTIS_SEARCH_MODEL` | – | `Xenova/paraphrase-multilingual-MiniLM-L12-v2` | Embedding model name (downloaded once on first use, ~80 MB) |
|
|
76
|
+
| `MANTIS_SEARCH_THREADS` | – | `1` | Number of ONNX intra-op threads for the embedding model. Default is 1 to prevent CPU saturation on multi-core machines and WSL. Increase only if index rebuild speed matters and the host is dedicated to this workload. |
|
|
76
77
|
|
|
77
78
|
### Config file (fallback)
|
|
78
79
|
|
package/dist/config.js
CHANGED
|
@@ -76,6 +76,7 @@ export async function getConfig() {
|
|
|
76
76
|
const searchDir = process.env.MANTIS_SEARCH_DIR ?? join(cacheDir, 'search');
|
|
77
77
|
const searchModelName = process.env.MANTIS_SEARCH_MODEL ??
|
|
78
78
|
'Xenova/paraphrase-multilingual-MiniLM-L12-v2';
|
|
79
|
+
const searchNumThreads = Math.max(1, parseInt(process.env.MANTIS_SEARCH_THREADS ?? '', 10) || 1);
|
|
79
80
|
cachedConfig = {
|
|
80
81
|
baseUrl: baseUrl.replace(/\/$/, ''), // strip trailing slash
|
|
81
82
|
apiKey,
|
|
@@ -86,6 +87,7 @@ export async function getConfig() {
|
|
|
86
87
|
backend: searchBackend,
|
|
87
88
|
dir: searchDir,
|
|
88
89
|
modelName: searchModelName,
|
|
90
|
+
numThreads: searchNumThreads,
|
|
89
91
|
},
|
|
90
92
|
};
|
|
91
93
|
return cachedConfig;
|
package/dist/index.js
CHANGED
|
@@ -69,6 +69,10 @@ async function runStdio() {
|
|
|
69
69
|
const transport = new StdioServerTransport();
|
|
70
70
|
await server.connect(transport);
|
|
71
71
|
console.error(`MantisBT MCP Server v${version} running (stdio)`);
|
|
72
|
+
// Exit when the parent process (Claude) closes stdin. Without this, a
|
|
73
|
+
// background search sync keeps the process alive after the session ends,
|
|
74
|
+
// causing multiple stale instances to accumulate over time.
|
|
75
|
+
process.stdin.once('close', () => process.exit(0));
|
|
72
76
|
}
|
|
73
77
|
async function runHttp() {
|
|
74
78
|
const server = await createMcpServer();
|
package/dist/search/embedder.js
CHANGED
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
export class Embedder {
|
|
10
10
|
modelName;
|
|
11
|
+
numThreads;
|
|
11
12
|
pipe = null;
|
|
12
|
-
constructor(modelName) {
|
|
13
|
+
constructor(modelName, numThreads = 1) {
|
|
13
14
|
this.modelName = modelName;
|
|
15
|
+
this.numThreads = numThreads;
|
|
14
16
|
}
|
|
15
17
|
async load() {
|
|
16
18
|
if (this.pipe)
|
|
17
19
|
return this.pipe;
|
|
18
|
-
process.stderr.write(`[mantisbt-search] Loading embedding model ${this.modelName}...\n`);
|
|
20
|
+
process.stderr.write(`[mantisbt-search] Loading embedding model ${this.modelName} (threads: ${this.numThreads})...\n`);
|
|
19
21
|
let transformers;
|
|
20
22
|
try {
|
|
21
23
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -25,7 +27,12 @@ export class Embedder {
|
|
|
25
27
|
const msg = err instanceof Error ? err.message : String(err);
|
|
26
28
|
throw new Error(`Failed to load @huggingface/transformers: ${msg}`);
|
|
27
29
|
}
|
|
28
|
-
this.pipe = await transformers.pipeline('feature-extraction', this.modelName
|
|
30
|
+
this.pipe = await transformers.pipeline('feature-extraction', this.modelName, {
|
|
31
|
+
session_options: {
|
|
32
|
+
intra_op_num_threads: this.numThreads,
|
|
33
|
+
inter_op_num_threads: 1, // Transformer graphs are sequential — no benefit from inter-op parallelism
|
|
34
|
+
},
|
|
35
|
+
});
|
|
29
36
|
return this.pipe;
|
|
30
37
|
}
|
|
31
38
|
async embed(text) {
|
package/dist/search/index.js
CHANGED
|
@@ -9,7 +9,7 @@ export async function initializeSearchModule(server, client, config) {
|
|
|
9
9
|
if (!config.enabled)
|
|
10
10
|
return;
|
|
11
11
|
const store = createVectorStore(config.backend, config.dir);
|
|
12
|
-
const embedder = new Embedder(config.modelName);
|
|
12
|
+
const embedder = new Embedder(config.modelName, config.numThreads);
|
|
13
13
|
registerSearchTools(server, client, store, embedder);
|
|
14
14
|
// Pre-initialize lastKnownTotal so get_search_index_status shows a value
|
|
15
15
|
// immediately on startup, even while the background sync is still running.
|
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { Embedder } from '../../src/search/embedder.js';
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Mock @huggingface/transformers (dynamic import)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
const mockPipelineFn = vi.fn(async (texts: string | string[]) => {
|
|
9
|
+
if (Array.isArray(texts)) {
|
|
10
|
+
return texts.map(() => ({ data: new Float32Array(4).fill(0.1), dims: [1, 4] }));
|
|
11
|
+
}
|
|
12
|
+
return { data: new Float32Array(4).fill(0.1), dims: [4] };
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const mockPipelineFactory = vi.fn(async (_task: string, _model: string, _opts?: unknown) => mockPipelineFn);
|
|
16
|
+
|
|
17
|
+
vi.mock('@huggingface/transformers', () => ({
|
|
18
|
+
pipeline: mockPipelineFactory,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.clearAllMocks();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Thread configuration
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
describe('Embedder – thread configuration', () => {
|
|
30
|
+
it('passes intra_op_num_threads=1 by default', async () => {
|
|
31
|
+
const embedder = new Embedder('test-model');
|
|
32
|
+
await embedder.embed('hello');
|
|
33
|
+
|
|
34
|
+
expect(mockPipelineFactory).toHaveBeenCalledWith(
|
|
35
|
+
'feature-extraction',
|
|
36
|
+
'test-model',
|
|
37
|
+
expect.objectContaining({
|
|
38
|
+
session_options: { intra_op_num_threads: 1, inter_op_num_threads: 1 },
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('passes configured numThreads to intra_op_num_threads; inter stays 1', async () => {
|
|
44
|
+
const embedder = new Embedder('test-model', 4);
|
|
45
|
+
await embedder.embed('hello');
|
|
46
|
+
|
|
47
|
+
expect(mockPipelineFactory).toHaveBeenCalledWith(
|
|
48
|
+
'feature-extraction',
|
|
49
|
+
'test-model',
|
|
50
|
+
expect.objectContaining({
|
|
51
|
+
session_options: { intra_op_num_threads: 4, inter_op_num_threads: 1 },
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('loads the pipeline only once (lazy singleton)', async () => {
|
|
57
|
+
const embedder = new Embedder('test-model', 1);
|
|
58
|
+
await embedder.embed('first');
|
|
59
|
+
await embedder.embed('second');
|
|
60
|
+
|
|
61
|
+
expect(mockPipelineFactory).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Embedder default — numThreads omitted
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
describe('Embedder – numThreads default', () => {
|
|
70
|
+
it('uses intra_op_num_threads=1 when numThreads is not passed', async () => {
|
|
71
|
+
const embedder = new Embedder('m');
|
|
72
|
+
await embedder.embed('x');
|
|
73
|
+
expect(mockPipelineFactory).toHaveBeenCalledWith(
|
|
74
|
+
'feature-extraction',
|
|
75
|
+
'm',
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
session_options: expect.objectContaining({ intra_op_num_threads: 1 }),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
});
|
package/.env.local
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": 51,
|
|
3
|
-
"name": "domAgent",
|
|
4
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
5
|
-
"email": "d.pesch+agent@11com7.de",
|
|
6
|
-
"language": "german",
|
|
7
|
-
"timezone": "Europe/Berlin",
|
|
8
|
-
"access_level": {
|
|
9
|
-
"id": 70,
|
|
10
|
-
"name": "manager",
|
|
11
|
-
"label": "Manager"
|
|
12
|
-
},
|
|
13
|
-
"created_at": "2026-02-28T09:08:30+01:00",
|
|
14
|
-
"projects": [
|
|
15
|
-
{
|
|
16
|
-
"id": 30,
|
|
17
|
-
"name": "((Haus)) (Bolliggasse 1a)"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"id": 54,
|
|
21
|
-
"name": "11com7 Claude MetaRepo"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"id": 2,
|
|
25
|
-
"name": "11com7 ContentGo-Lib"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"id": 48,
|
|
29
|
-
"name": "11com7 DevServer"
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"id": 25,
|
|
33
|
-
"name": "11com7 GmbH"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"id": 39,
|
|
37
|
-
"name": "11com7 Hosting (Keyweb, Ansible)"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"id": 44,
|
|
41
|
-
"name": "11com7-Ausbildung-FIAE"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"id": 52,
|
|
45
|
-
"name": "11com7-DVMS (Datenschutzverletzungsmelder)"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"id": 5,
|
|
49
|
-
"name": "11com7-Homepage"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"id": 27,
|
|
53
|
-
"name": "b11com7"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"id": 3,
|
|
57
|
-
"name": "BIBB NAA309"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"id": 11,
|
|
61
|
-
"name": "BIBB wbmonitor"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"id": 38,
|
|
65
|
-
"name": "DSGV Budgetanalyse 3"
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"id": 28,
|
|
69
|
-
"name": "DSGV Finanzchecker (App)"
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"id": 45,
|
|
73
|
-
"name": "DSGV Finanzchecker (Landingpage)"
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"id": 40,
|
|
77
|
-
"name": "DSGV Login"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"id": 26,
|
|
81
|
-
"name": "DSGV Referenzbudgets"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"id": 8,
|
|
85
|
-
"name": "DSGV Vortragsservice"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"id": 14,
|
|
89
|
-
"name": "DSGV Web-Budgetplaner"
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"id": 42,
|
|
93
|
-
"name": "Lingua-World"
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"id": 49,
|
|
97
|
-
"name": "SIK M&E-App"
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"id": 21,
|
|
101
|
-
"name": "Uni Bonn (Hausprint V2)"
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
"id": 53,
|
|
105
|
-
"name": "ZEEEM GSM-Landingpage"
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"issues": [
|
|
3
|
-
{
|
|
4
|
-
"id": 7860,
|
|
5
|
-
"summary": "sync_metadata befüllt Kategorien und Tags nicht im Cache",
|
|
6
|
-
"description": "## Was ist passiert?\n`/mantis-sync` zeigt für alle Projekte \"Kategorien: 0\" und \"Tags: 0\" an, obwohl in Mantis Kategorien und Tags vorhanden sind.\n\nDirektaufruf von `get_project_categories(project_id: 25)` liefert 9 Kategorien für \"11com7 GmbH\" — der Cache enthält jedoch ein leeres `categories: []`. Tags fehlen im Cache-Root vollständig (kein `tags`-Key vorhanden).\n\n## Betroffene Komponente\n- Skill: `/mantis-sync`\n- Helper: `~/.claude/helpers/mantis-metadata-summary.js`\n- Externer Dependency: `mantisbt-mcp` Server, Tool `sync_metadata`\n- Plattform: beide\n\n## Ursachenanalyse\n`sync_metadata` im mantisbt-mcp-Server befüllt `byProject[id].categories` nicht — das Feld existiert im Schema, enthält aber immer `[]`. Der `tags`-Key wird auf Root-Ebene gar nicht angelegt.\n\nDas Summary-Script liest bereits korrekt:\n- `pd.categories || proj.categories || []` → bekommt immer `[]`\n- `metadata.tags || []` → bekommt immer `[]` (Key fehlt im Cache)\n\n## Lösungsansätze\n1. **mantisbt-mcp `sync_metadata` erweitern (bevorzugt):**\n - Für jedes Projekt `get_project_categories(project_id)` aufrufen und Ergebnis in `byProject[id].categories` speichern\n - `list_tags()` einmalig aufrufen und als `tags: [...]` auf Root-Ebene speichern\n2. **Workaround im /mantis-sync-Skill:** Kategorien und Tags per Direkt-API-Aufruf nachladen — unabhängig vom MCP-Server, aber teurer.\n\n## So reproduzierbar\n1. `/mantis-sync` ausführen\n2. Alle Projekte zeigen \"0\" in der Kategorien-Spalte, Fußzeile \"0 Tags\"\n3. Kontrollaufruf: `get_project_categories(project_id: 25)` → 9 Kategorien\n\n## Hinweise zur Umsetzung\n- `mantis-metadata-summary.js` muss NICHT geändert werden — liest bereits korrekt\n- Kategorien sind projektspezifisch → Sync muss über alle Projekte iterieren (erhöhter API-Aufwand)\n- Tags sind global → ein einzelner `list_tags()`-Aufruf reicht\n- Nach MCP-Server-Fix: `sync_metadata()` neu ausführen und Cache prüfen",
|
|
7
|
-
"project": {
|
|
8
|
-
"id": 54,
|
|
9
|
-
"name": "11com7 Claude MetaRepo"
|
|
10
|
-
},
|
|
11
|
-
"category": {
|
|
12
|
-
"id": 393,
|
|
13
|
-
"name": "Skills"
|
|
14
|
-
},
|
|
15
|
-
"reporter": {
|
|
16
|
-
"id": 51,
|
|
17
|
-
"name": "domAgent",
|
|
18
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
19
|
-
"email": "d.pesch+agent@11com7.de"
|
|
20
|
-
},
|
|
21
|
-
"handler": {
|
|
22
|
-
"id": 51,
|
|
23
|
-
"name": "domAgent",
|
|
24
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
25
|
-
"email": "d.pesch+agent@11com7.de"
|
|
26
|
-
},
|
|
27
|
-
"status": {
|
|
28
|
-
"id": 50,
|
|
29
|
-
"name": "assigned",
|
|
30
|
-
"label": "zugewiesen",
|
|
31
|
-
"color": "#afbed5"
|
|
32
|
-
},
|
|
33
|
-
"resolution": {
|
|
34
|
-
"id": 10,
|
|
35
|
-
"name": "open",
|
|
36
|
-
"label": "offen"
|
|
37
|
-
},
|
|
38
|
-
"view_state": {
|
|
39
|
-
"id": 10,
|
|
40
|
-
"name": "public",
|
|
41
|
-
"label": "öffentlich"
|
|
42
|
-
},
|
|
43
|
-
"priority": {
|
|
44
|
-
"id": 30,
|
|
45
|
-
"name": "normal",
|
|
46
|
-
"label": "normal"
|
|
47
|
-
},
|
|
48
|
-
"severity": {
|
|
49
|
-
"id": 200,
|
|
50
|
-
"name": "Technische Schuld",
|
|
51
|
-
"label": "Technische Schuld"
|
|
52
|
-
},
|
|
53
|
-
"reproducibility": {
|
|
54
|
-
"id": 70,
|
|
55
|
-
"name": "have not tried",
|
|
56
|
-
"label": "nicht getestet"
|
|
57
|
-
},
|
|
58
|
-
"sticky": false,
|
|
59
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
60
|
-
"updated_at": "2026-03-16T15:40:38+01:00",
|
|
61
|
-
"history": [
|
|
62
|
-
{
|
|
63
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
64
|
-
"user": {
|
|
65
|
-
"id": 51,
|
|
66
|
-
"name": "domAgent",
|
|
67
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
68
|
-
"email": "d.pesch+agent@11com7.de"
|
|
69
|
-
},
|
|
70
|
-
"type": {
|
|
71
|
-
"id": 1,
|
|
72
|
-
"name": "issue-new"
|
|
73
|
-
},
|
|
74
|
-
"message": "Neuer Eintrag"
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
78
|
-
"user": {
|
|
79
|
-
"id": 51,
|
|
80
|
-
"name": "domAgent",
|
|
81
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
82
|
-
"email": "d.pesch+agent@11com7.de"
|
|
83
|
-
},
|
|
84
|
-
"field": {
|
|
85
|
-
"name": "status",
|
|
86
|
-
"label": "Status"
|
|
87
|
-
},
|
|
88
|
-
"type": {
|
|
89
|
-
"id": 0,
|
|
90
|
-
"name": "field-updated"
|
|
91
|
-
},
|
|
92
|
-
"old_value": {
|
|
93
|
-
"id": 10,
|
|
94
|
-
"name": "new",
|
|
95
|
-
"label": "neu",
|
|
96
|
-
"color": "#eeb3aa"
|
|
97
|
-
},
|
|
98
|
-
"new_value": {
|
|
99
|
-
"id": 50,
|
|
100
|
-
"name": "assigned",
|
|
101
|
-
"label": "zugewiesen",
|
|
102
|
-
"color": "#afbed5"
|
|
103
|
-
},
|
|
104
|
-
"message": "Status",
|
|
105
|
-
"change": "neu => zugewiesen"
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
109
|
-
"user": {
|
|
110
|
-
"id": 51,
|
|
111
|
-
"name": "domAgent",
|
|
112
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
113
|
-
"email": "d.pesch+agent@11com7.de"
|
|
114
|
-
},
|
|
115
|
-
"field": {
|
|
116
|
-
"name": "handler",
|
|
117
|
-
"label": "Bearbeitung durch"
|
|
118
|
-
},
|
|
119
|
-
"type": {
|
|
120
|
-
"id": 0,
|
|
121
|
-
"name": "field-updated"
|
|
122
|
-
},
|
|
123
|
-
"old_value": {
|
|
124
|
-
"id": 0
|
|
125
|
-
},
|
|
126
|
-
"new_value": {
|
|
127
|
-
"id": 51,
|
|
128
|
-
"name": "domAgent",
|
|
129
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
130
|
-
"email": "d.pesch+agent@11com7.de"
|
|
131
|
-
},
|
|
132
|
-
"message": "Bearbeitung durch",
|
|
133
|
-
"change": " => domAgent"
|
|
134
|
-
}
|
|
135
|
-
]
|
|
136
|
-
}
|
|
137
|
-
]
|
|
138
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"configs": [
|
|
3
|
-
{
|
|
4
|
-
"option": "severity_enum_string",
|
|
5
|
-
"value": [
|
|
6
|
-
{ "id": 10, "name": "Feature-Wunsch", "label": "Feature-Wunsch" },
|
|
7
|
-
{ "id": 20, "name": "Trivial", "label": "Trivial" },
|
|
8
|
-
{ "id": 30, "name": "Fehler im Text", "label": "Fehler im Text" },
|
|
9
|
-
{ "id": 40, "name": "Unschönheit", "label": "Unschönheit" },
|
|
10
|
-
{ "id": 50, "name": "kleinerer Fehler", "label": "kleinerer Fehler" },
|
|
11
|
-
{ "id": 60, "name": "schwerer Fehler", "label": "schwerer Fehler" },
|
|
12
|
-
{ "id": 70, "name": "Absturz", "label": "Absturz" },
|
|
13
|
-
{ "id": 80, "name": "Blocker", "label": "Blocker" },
|
|
14
|
-
{ "id": 200, "name": "Technische Schuld", "label": "Technische Schuld" },
|
|
15
|
-
{ "id": 210, "name": "Wartung", "label": "Wartung" }
|
|
16
|
-
]
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"option": "status_enum_string",
|
|
20
|
-
"value": [
|
|
21
|
-
{ "id": 10, "name": "new", "label": "neu" },
|
|
22
|
-
{ "id": 20, "name": "feedback", "label": "Rückmeldung" },
|
|
23
|
-
{ "id": 30, "name": "acknowledged", "label": "anerkannt" },
|
|
24
|
-
{ "id": 40, "name": "confirmed", "label": "bestätigt" },
|
|
25
|
-
{ "id": 50, "name": "assigned", "label": "zugewiesen" },
|
|
26
|
-
{ "id": 80, "name": "resolved", "label": "erledigt" },
|
|
27
|
-
{ "id": 90, "name": "closed", "label": "geschlossen" }
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"option": "priority_enum_string",
|
|
32
|
-
"value": [
|
|
33
|
-
{ "id": 10, "name": "none", "label": "keine" },
|
|
34
|
-
{ "id": 20, "name": "low", "label": "niedrig" },
|
|
35
|
-
{ "id": 30, "name": "normal", "label": "normal" },
|
|
36
|
-
{ "id": 40, "name": "high", "label": "hoch" },
|
|
37
|
-
{ "id": 50, "name": "urgent", "label": "dringend" },
|
|
38
|
-
{ "id": 60, "name": "immediate", "label": "sofort" }
|
|
39
|
-
]
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"option": "resolution_enum_string",
|
|
43
|
-
"value": [
|
|
44
|
-
{ "id": 10, "name": "open", "label": "offen" },
|
|
45
|
-
{ "id": 20, "name": "fixed", "label": "erledigt" },
|
|
46
|
-
{ "id": 30, "name": "reopened", "label": "wiedereröffnet" },
|
|
47
|
-
{ "id": 40, "name": "unable to duplicate", "label": "nicht reproduzierbar" },
|
|
48
|
-
{ "id": 50, "name": "not fixable", "label": "unlösbar" },
|
|
49
|
-
{ "id": 60, "name": "duplicate", "label": "doppelt" },
|
|
50
|
-
{ "id": 70, "name": "not a bug", "label": "keine Änderung notwendig" },
|
|
51
|
-
{ "id": 80, "name": "suspended", "label": "aufgeschoben" },
|
|
52
|
-
{ "id": 90, "name": "wont fix", "label": "wird nicht behoben" }
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"option": "reproducibility_enum_string",
|
|
57
|
-
"value": [
|
|
58
|
-
{ "id": 10, "name": "always", "label": "immer" },
|
|
59
|
-
{ "id": 30, "name": "sometimes", "label": "manchmal" },
|
|
60
|
-
{ "id": 50, "name": "random", "label": "zufällig" },
|
|
61
|
-
{ "id": 70, "name": "have not tried", "label": "nicht getestet" },
|
|
62
|
-
{ "id": 90, "name": "unable to duplicate", "label": "nicht reproduzierbar" },
|
|
63
|
-
{ "id": 100, "name": "N/A", "label": "N/A" }
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"issues": [
|
|
3
|
-
{
|
|
4
|
-
"id": 7860,
|
|
5
|
-
"summary": "sync_metadata befüllt Kategorien und Tags nicht im Cache",
|
|
6
|
-
"description": "## Was ist passiert?\n`/mantis-sync` zeigt für alle Projekte \"Kategorien: 0\" und \"Tags: 0\" an, obwohl in Mantis Kategorien und Tags vorhanden sind.\n\nDirektaufruf von `get_project_categories(project_id: 25)` liefert 9 Kategorien für \"11com7 GmbH\" — der Cache enthält jedoch ein leeres `categories: []`. Tags fehlen im Cache-Root vollständig (kein `tags`-Key vorhanden).\n\n## Betroffene Komponente\n- Skill: `/mantis-sync`\n- Helper: `~/.claude/helpers/mantis-metadata-summary.js`\n- Externer Dependency: `mantisbt-mcp` Server, Tool `sync_metadata`\n- Plattform: beide\n\n## Ursachenanalyse\n`sync_metadata` im mantisbt-mcp-Server befüllt `byProject[id].categories` nicht — das Feld existiert im Schema, enthält aber immer `[]`. Der `tags`-Key wird auf Root-Ebene gar nicht angelegt.\n\nDas Summary-Script liest bereits korrekt:\n- `pd.categories || proj.categories || []` → bekommt immer `[]`\n- `metadata.tags || []` → bekommt immer `[]` (Key fehlt im Cache)\n\n## Lösungsansätze\n1. **mantisbt-mcp `sync_metadata` erweitern (bevorzugt):**\n - Für jedes Projekt `get_project_categories(project_id)` aufrufen und Ergebnis in `byProject[id].categories` speichern\n - `list_tags()` einmalig aufrufen und als `tags: [...]` auf Root-Ebene speichern\n2. **Workaround im /mantis-sync-Skill:** Kategorien und Tags per Direkt-API-Aufruf nachladen — unabhängig vom MCP-Server, aber teurer.\n\n## So reproduzierbar\n1. `/mantis-sync` ausführen\n2. Alle Projekte zeigen \"0\" in der Kategorien-Spalte, Fußzeile \"0 Tags\"\n3. Kontrollaufruf: `get_project_categories(project_id: 25)` → 9 Kategorien\n\n## Hinweise zur Umsetzung\n- `mantis-metadata-summary.js` muss NICHT geändert werden — liest bereits korrekt\n- Kategorien sind projektspezifisch → Sync muss über alle Projekte iterieren (erhöhter API-Aufwand)\n- Tags sind global → ein einzelner `list_tags()`-Aufruf reicht\n- Nach MCP-Server-Fix: `sync_metadata()` neu ausführen und Cache prüfen",
|
|
7
|
-
"project": {
|
|
8
|
-
"id": 54,
|
|
9
|
-
"name": "11com7 Claude MetaRepo"
|
|
10
|
-
},
|
|
11
|
-
"category": {
|
|
12
|
-
"id": 393,
|
|
13
|
-
"name": "Skills"
|
|
14
|
-
},
|
|
15
|
-
"reporter": {
|
|
16
|
-
"id": 51,
|
|
17
|
-
"name": "domAgent",
|
|
18
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
19
|
-
"email": "d.pesch+agent@11com7.de"
|
|
20
|
-
},
|
|
21
|
-
"handler": {
|
|
22
|
-
"id": 51,
|
|
23
|
-
"name": "domAgent",
|
|
24
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
25
|
-
"email": "d.pesch+agent@11com7.de"
|
|
26
|
-
},
|
|
27
|
-
"status": {
|
|
28
|
-
"id": 50,
|
|
29
|
-
"name": "assigned",
|
|
30
|
-
"label": "zugewiesen",
|
|
31
|
-
"color": "#afbed5"
|
|
32
|
-
},
|
|
33
|
-
"resolution": {
|
|
34
|
-
"id": 10,
|
|
35
|
-
"name": "open",
|
|
36
|
-
"label": "offen"
|
|
37
|
-
},
|
|
38
|
-
"view_state": {
|
|
39
|
-
"id": 10,
|
|
40
|
-
"name": "public",
|
|
41
|
-
"label": "öffentlich"
|
|
42
|
-
},
|
|
43
|
-
"priority": {
|
|
44
|
-
"id": 30,
|
|
45
|
-
"name": "normal",
|
|
46
|
-
"label": "normal"
|
|
47
|
-
},
|
|
48
|
-
"severity": {
|
|
49
|
-
"id": 200,
|
|
50
|
-
"name": "Technische Schuld",
|
|
51
|
-
"label": "Technische Schuld"
|
|
52
|
-
},
|
|
53
|
-
"reproducibility": {
|
|
54
|
-
"id": 70,
|
|
55
|
-
"name": "have not tried",
|
|
56
|
-
"label": "nicht getestet"
|
|
57
|
-
},
|
|
58
|
-
"sticky": false,
|
|
59
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
60
|
-
"updated_at": "2026-03-16T15:40:38+01:00",
|
|
61
|
-
"history": [
|
|
62
|
-
{
|
|
63
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
64
|
-
"user": {
|
|
65
|
-
"id": 51,
|
|
66
|
-
"name": "domAgent",
|
|
67
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
68
|
-
"email": "d.pesch+agent@11com7.de"
|
|
69
|
-
},
|
|
70
|
-
"type": {
|
|
71
|
-
"id": 1,
|
|
72
|
-
"name": "issue-new"
|
|
73
|
-
},
|
|
74
|
-
"message": "Neuer Eintrag"
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
78
|
-
"user": {
|
|
79
|
-
"id": 51,
|
|
80
|
-
"name": "domAgent",
|
|
81
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
82
|
-
"email": "d.pesch+agent@11com7.de"
|
|
83
|
-
},
|
|
84
|
-
"field": {
|
|
85
|
-
"name": "status",
|
|
86
|
-
"label": "Status"
|
|
87
|
-
},
|
|
88
|
-
"type": {
|
|
89
|
-
"id": 0,
|
|
90
|
-
"name": "field-updated"
|
|
91
|
-
},
|
|
92
|
-
"old_value": {
|
|
93
|
-
"id": 10,
|
|
94
|
-
"name": "new",
|
|
95
|
-
"label": "neu",
|
|
96
|
-
"color": "#eeb3aa"
|
|
97
|
-
},
|
|
98
|
-
"new_value": {
|
|
99
|
-
"id": 50,
|
|
100
|
-
"name": "assigned",
|
|
101
|
-
"label": "zugewiesen",
|
|
102
|
-
"color": "#afbed5"
|
|
103
|
-
},
|
|
104
|
-
"message": "Status",
|
|
105
|
-
"change": "neu => zugewiesen"
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"created_at": "2026-03-16T15:40:38+01:00",
|
|
109
|
-
"user": {
|
|
110
|
-
"id": 51,
|
|
111
|
-
"name": "domAgent",
|
|
112
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
113
|
-
"email": "d.pesch+agent@11com7.de"
|
|
114
|
-
},
|
|
115
|
-
"field": {
|
|
116
|
-
"name": "handler",
|
|
117
|
-
"label": "Bearbeitung durch"
|
|
118
|
-
},
|
|
119
|
-
"type": {
|
|
120
|
-
"id": 0,
|
|
121
|
-
"name": "field-updated"
|
|
122
|
-
},
|
|
123
|
-
"old_value": {
|
|
124
|
-
"id": 0
|
|
125
|
-
},
|
|
126
|
-
"new_value": {
|
|
127
|
-
"id": 51,
|
|
128
|
-
"name": "domAgent",
|
|
129
|
-
"real_name": "Dominik Pesch (via Agent)",
|
|
130
|
-
"email": "d.pesch+agent@11com7.de"
|
|
131
|
-
},
|
|
132
|
-
"message": "Bearbeitung durch",
|
|
133
|
-
"change": " => domAgent"
|
|
134
|
-
}
|
|
135
|
-
]
|
|
136
|
-
}
|
|
137
|
-
]
|
|
138
|
-
}
|