@dpesch/mantisbt-mcp-server 1.0.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.
Files changed (49) hide show
  1. package/.env.local +2 -0
  2. package/CHANGELOG.md +68 -0
  3. package/LICENSE +21 -0
  4. package/README.de.md +177 -0
  5. package/README.md +177 -0
  6. package/dist/cache.js +52 -0
  7. package/dist/client.js +114 -0
  8. package/dist/config.js +54 -0
  9. package/dist/constants.js +23 -0
  10. package/dist/index.js +120 -0
  11. package/dist/tools/config.js +107 -0
  12. package/dist/tools/files.js +37 -0
  13. package/dist/tools/filters.js +35 -0
  14. package/dist/tools/issues.js +191 -0
  15. package/dist/tools/metadata.js +119 -0
  16. package/dist/tools/monitors.js +38 -0
  17. package/dist/tools/notes.js +96 -0
  18. package/dist/tools/projects.js +127 -0
  19. package/dist/tools/relationships.js +54 -0
  20. package/dist/tools/tags.js +78 -0
  21. package/dist/tools/users.js +34 -0
  22. package/dist/tools/version.js +58 -0
  23. package/dist/types.js +4 -0
  24. package/dist/version-hint.js +117 -0
  25. package/package.json +41 -0
  26. package/scripts/record-fixtures.ts +138 -0
  27. package/tests/cache.test.ts +149 -0
  28. package/tests/client.test.ts +241 -0
  29. package/tests/config.test.ts +164 -0
  30. package/tests/fixtures/get_current_user.json +39 -0
  31. package/tests/fixtures/get_issue.json +151 -0
  32. package/tests/fixtures/get_project_categories.json +60 -0
  33. package/tests/fixtures/get_project_versions.json +3 -0
  34. package/tests/fixtures/get_project_versions_with_data.json +28 -0
  35. package/tests/fixtures/list_issues.json +67 -0
  36. package/tests/fixtures/list_projects.json +65 -0
  37. package/tests/fixtures/recorded/get_current_user.json +108 -0
  38. package/tests/fixtures/recorded/get_issue.json +320 -0
  39. package/tests/fixtures/recorded/get_project_categories.json +241 -0
  40. package/tests/fixtures/recorded/get_project_versions.json +3 -0
  41. package/tests/fixtures/recorded/list_issues.json +824 -0
  42. package/tests/fixtures/recorded/list_projects.json +10641 -0
  43. package/tests/helpers/mock-server.ts +32 -0
  44. package/tests/tools/issues.test.ts +130 -0
  45. package/tests/tools/projects.test.ts +169 -0
  46. package/tests/tools/users.test.ts +76 -0
  47. package/tests/version-hint.test.ts +230 -0
  48. package/tsconfig.build.json +8 -0
  49. package/vitest.config.ts +8 -0
package/.env.local ADDED
@@ -0,0 +1,2 @@
1
+ MANTIS_BASE_URL=https://mantis.dev.11com7.de
2
+ MANTIS_API_KEY=3vhKDwxVavmCzRUR7Cp0Lk7F8E4dha6n
package/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
+ This project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ---
9
+
10
+ ## [1.0.0] – 2026-03-15
11
+
12
+ First stable release.
13
+
14
+ ### Added
15
+
16
+ **Issues**
17
+ - `get_issue` — retrieve an issue by numeric ID
18
+ - `list_issues` — filter issues by project, status, author, page, and page size
19
+ - `create_issue` — create a new issue
20
+ - `update_issue` — update an existing issue
21
+ - `delete_issue` — delete an issue
22
+
23
+ **Notes**
24
+ - `list_notes` — list notes of an issue
25
+ - `add_note` — add a note to an issue
26
+ - `delete_note` — delete a note
27
+
28
+ **Attachments**
29
+ - `list_issue_files` — list attachments of an issue
30
+
31
+ **Relationships**
32
+ - `add_relationship` — create a relationship between two issues
33
+
34
+ **Monitors**
35
+ - `add_monitor` — add yourself as a monitor of an issue
36
+
37
+ **Tags**
38
+ - `list_tags` — list all available tags
39
+ - `attach_tags` — attach tags to an issue
40
+ - `detach_tag` — remove a tag from an issue
41
+
42
+ **Projects**
43
+ - `list_projects` — list all accessible projects
44
+ - `get_project_versions` — get versions of a project
45
+ - `get_project_categories` — get categories of a project
46
+ - `get_project_users` — get users of a project
47
+
48
+ **Metadata & System**
49
+ - `get_metadata` — retrieve cached metadata (projects, users, versions, categories)
50
+ - `sync_metadata` — refresh the metadata cache
51
+ - `list_filters` — list saved filters
52
+ - `get_current_user` — retrieve your own user profile
53
+ - `list_languages` — list available languages
54
+ - `get_config` — show server configuration (base URL, cache TTL)
55
+ - `get_mantis_version` — get MantisBT version and check for updates on GitHub
56
+
57
+ **Infrastructure**
58
+ - stdio and HTTP transport (Streamable HTTP)
59
+ - Metadata cache with configurable TTL (default: 1 hour)
60
+ - Config file fallback (`~/.claude/mantis.json`)
61
+ - `VersionHintService`: appends update hint to API error messages
62
+ - Vitest test suite with 73 unit and fixture-based tests
63
+ - MIT license, `CONTRIBUTING.md`, `README.md`
64
+
65
+ ### Fixed
66
+ - `get_project_categories` was calling `/projects/{id}/categories` (does not exist in MantisBT) — corrected to `GET projects/{id}` with extraction of `.projects[0].categories`
67
+
68
+ [1.0.0]: https://codeberg.org/dpesch/mantisbt-mcp-server/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dominik Pesch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.de.md ADDED
@@ -0,0 +1,177 @@
1
+ # MantisBT MCP Server
2
+
3
+ Ein [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) Server, der die [MantisBT REST API](https://documenter.getpostman.com/view/29959/mantis-bug-tracker-rest-api) in Claude Code und andere MCP-fähige Clients integriert. Issues lesen, erstellen und bearbeiten – direkt aus dem Editor heraus.
4
+
5
+ ## Voraussetzungen
6
+
7
+ - Node.js ≥ 18
8
+ - MantisBT-Installation mit aktivierter REST-API (ab Version 2.23)
9
+ - MantisBT API-Token (unter *Mein Konto → API-Token* erstellen)
10
+
11
+ ## Installation
12
+
13
+ **Via npx (empfohlen):**
14
+
15
+ In `~/.claude/claude_desktop_config.json` (Claude Desktop) oder der lokalen
16
+ `claude_desktop_config.json` (Claude Code) eintragen:
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "mantisbt": {
22
+ "command": "npx",
23
+ "args": ["-y", "@dpesch/mantisbt-mcp-server"],
24
+ "env": {
25
+ "MANTIS_BASE_URL": "https://deine-mantis-instanz.example.com/api/rest",
26
+ "MANTIS_API_KEY": "dein-api-token"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ **Lokaler Build:**
34
+
35
+ ```bash
36
+ git clone https://codeberg.org/dpesch/mantisbt-mcp-server
37
+ cd mantisbt-mcp-server
38
+ npm install
39
+ npm run build
40
+ ```
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "mantisbt": {
46
+ "command": "node",
47
+ "args": ["/pfad/zum/mantisbt-mcp-server/dist/index.js"],
48
+ "env": {
49
+ "MANTIS_BASE_URL": "https://deine-mantis-instanz.example.com/api/rest",
50
+ "MANTIS_API_KEY": "dein-api-token"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Konfiguration
58
+
59
+ ### Umgebungsvariablen
60
+
61
+ | Variable | Pflicht | Standard | Beschreibung |
62
+ |---|---|---|---|
63
+ | `MANTIS_BASE_URL` | ✅ | – | Basis-URL der MantisBT REST API |
64
+ | `MANTIS_API_KEY` | ✅ | – | API-Token für die Authentifizierung |
65
+ | `MANTIS_CACHE_DIR` | – | `~/.cache/mantisbt-mcp` | Verzeichnis für den Metadaten-Cache |
66
+ | `MANTIS_CACHE_TTL` | – | `3600` | Cache-Lebensdauer in Sekunden |
67
+ | `TRANSPORT` | – | `stdio` | Transport-Modus: `stdio` oder `http` |
68
+ | `PORT` | – | `3000` | Port für HTTP-Modus |
69
+
70
+ ### Config-Datei (Fallback)
71
+
72
+ Falls keine Umgebungsvariablen gesetzt sind, wird `~/.claude/mantis.json` ausgelesen:
73
+
74
+ ```json
75
+ {
76
+ "base_url": "https://deine-mantis-instanz.example.com/api/rest",
77
+ "api_key": "dein-api-token"
78
+ }
79
+ ```
80
+
81
+ ## Verfügbare Tools
82
+
83
+ ### Issues
84
+
85
+ | Tool | Beschreibung |
86
+ |---|---|
87
+ | `get_issue` | Ein Issue anhand seiner ID abrufen |
88
+ | `list_issues` | Issues nach Projekt, Status, Autor u.v.m. filtern |
89
+ | `create_issue` | Neues Issue anlegen |
90
+ | `update_issue` | Bestehendes Issue bearbeiten |
91
+ | `delete_issue` | Issue löschen |
92
+
93
+ ### Notizen
94
+
95
+ | Tool | Beschreibung |
96
+ |---|---|
97
+ | `list_notes` | Alle Notizen eines Issues auflisten |
98
+ | `add_note` | Notiz zu einem Issue hinzufügen |
99
+ | `delete_note` | Notiz löschen |
100
+
101
+ ### Anhänge
102
+
103
+ | Tool | Beschreibung |
104
+ |---|---|
105
+ | `list_issue_files` | Anhänge eines Issues auflisten |
106
+
107
+ ### Beziehungen
108
+
109
+ | Tool | Beschreibung |
110
+ |---|---|
111
+ | `add_relationship` | Beziehung zwischen zwei Issues erstellen |
112
+
113
+ ### Beobachter
114
+
115
+ | Tool | Beschreibung |
116
+ |---|---|
117
+ | `add_monitor` | Sich selbst als Beobachter eines Issues eintragen |
118
+
119
+ ### Tags
120
+
121
+ | Tool | Beschreibung |
122
+ |---|---|
123
+ | `list_tags` | Alle verfügbaren Tags auflisten |
124
+ | `attach_tags` | Tags an ein Issue hängen |
125
+ | `detach_tag` | Tag von einem Issue entfernen |
126
+
127
+ ### Projekte
128
+
129
+ | Tool | Beschreibung |
130
+ |---|---|
131
+ | `list_projects` | Alle zugänglichen Projekte auflisten |
132
+ | `get_project_versions` | Versionen eines Projekts abrufen |
133
+ | `get_project_categories` | Kategorien eines Projekts abrufen |
134
+ | `get_project_users` | Benutzer eines Projekts abrufen |
135
+
136
+ ### Metadaten & System
137
+
138
+ | Tool | Beschreibung |
139
+ |---|---|
140
+ | `get_metadata` | Gecachte Metadaten abrufen (Projekte, Benutzer, Versionen, Kategorien) |
141
+ | `sync_metadata` | Metadaten-Cache neu befüllen |
142
+ | `list_filters` | Gespeicherte Filter auflisten |
143
+ | `get_current_user` | Eigenes Benutzerprofil abrufen |
144
+ | `list_languages` | Verfügbare Sprachen auflisten |
145
+ | `get_config` | Server-Konfiguration (Basis-URL, Cache-TTL) anzeigen |
146
+ | `get_mantis_version` | MantisBT-Version abrufen und auf Updates prüfen |
147
+
148
+ ## HTTP-Modus
149
+
150
+ Für den Einsatz als eigenständiger Server (z.B. in Remote-Setups):
151
+
152
+ ```bash
153
+ MANTIS_BASE_URL=... MANTIS_API_KEY=... TRANSPORT=http PORT=3456 node dist/index.js
154
+ ```
155
+
156
+ Healthcheck: `GET http://localhost:3456/health`
157
+
158
+ ## Entwicklung
159
+
160
+ ```bash
161
+ npm install # Abhängigkeiten installieren
162
+ npm run build # TypeScript → dist/ kompilieren
163
+ npm run typecheck # Typprüfung ohne Ausgabe
164
+ npm run dev # Watch-Modus für Entwicklung
165
+ npm test # Tests ausführen (vitest)
166
+ npm run test:watch # Tests im Watch-Modus
167
+ npm run test:coverage # Coverage-Report
168
+ ```
169
+
170
+ ## Lizenz
171
+
172
+ MIT – siehe [LICENSE](LICENSE)
173
+
174
+ ## Mitwirken
175
+
176
+ Beiträge willkommen! Bitte [CONTRIBUTING.md](CONTRIBUTING.md) lesen.
177
+ Repository: [codeberg.org/dpesch/mantisbt-mcp-server](https://codeberg.org/dpesch/mantisbt-mcp-server)
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # MantisBT MCP Server
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that integrates the [MantisBT REST API](https://documenter.getpostman.com/view/29959/mantis-bug-tracker-rest-api) into Claude Code and other MCP-capable clients. Read, create, and update issues directly from your editor.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js ≥ 18
8
+ - MantisBT installation with REST API enabled (version 2.23+)
9
+ - MantisBT API token (create under *My Account → API Tokens*)
10
+
11
+ ## Installation
12
+
13
+ **Via npx (recommended):**
14
+
15
+ Add to `~/.claude/claude_desktop_config.json` (Claude Desktop) or your local
16
+ `claude_desktop_config.json` (Claude Code):
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "mantisbt": {
22
+ "command": "npx",
23
+ "args": ["-y", "@dpesch/mantisbt-mcp-server"],
24
+ "env": {
25
+ "MANTIS_BASE_URL": "https://your-mantis.example.com/api/rest",
26
+ "MANTIS_API_KEY": "your-api-token"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ **Local build:**
34
+
35
+ ```bash
36
+ git clone https://codeberg.org/dpesch/mantisbt-mcp-server
37
+ cd mantisbt-mcp-server
38
+ npm install
39
+ npm run build
40
+ ```
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "mantisbt": {
46
+ "command": "node",
47
+ "args": ["/path/to/mantisbt-mcp-server/dist/index.js"],
48
+ "env": {
49
+ "MANTIS_BASE_URL": "https://your-mantis.example.com/api/rest",
50
+ "MANTIS_API_KEY": "your-api-token"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Configuration
58
+
59
+ ### Environment variables
60
+
61
+ | Variable | Required | Default | Description |
62
+ |---|---|---|---|
63
+ | `MANTIS_BASE_URL` | ✅ | – | Base URL of the MantisBT REST API |
64
+ | `MANTIS_API_KEY` | ✅ | – | API token for authentication |
65
+ | `MANTIS_CACHE_DIR` | – | `~/.cache/mantisbt-mcp` | Directory for the metadata cache |
66
+ | `MANTIS_CACHE_TTL` | – | `3600` | Cache lifetime in seconds |
67
+ | `TRANSPORT` | – | `stdio` | Transport mode: `stdio` or `http` |
68
+ | `PORT` | – | `3000` | Port for HTTP mode |
69
+
70
+ ### Config file (fallback)
71
+
72
+ If no environment variables are set, `~/.claude/mantis.json` is read:
73
+
74
+ ```json
75
+ {
76
+ "base_url": "https://your-mantis.example.com/api/rest",
77
+ "api_key": "your-api-token"
78
+ }
79
+ ```
80
+
81
+ ## Available tools
82
+
83
+ ### Issues
84
+
85
+ | Tool | Description |
86
+ |---|---|
87
+ | `get_issue` | Retrieve an issue by its numeric ID |
88
+ | `list_issues` | Filter issues by project, status, author, and more |
89
+ | `create_issue` | Create a new issue |
90
+ | `update_issue` | Update an existing issue |
91
+ | `delete_issue` | Delete an issue |
92
+
93
+ ### Notes
94
+
95
+ | Tool | Description |
96
+ |---|---|
97
+ | `list_notes` | List all notes of an issue |
98
+ | `add_note` | Add a note to an issue |
99
+ | `delete_note` | Delete a note |
100
+
101
+ ### Attachments
102
+
103
+ | Tool | Description |
104
+ |---|---|
105
+ | `list_issue_files` | List attachments of an issue |
106
+
107
+ ### Relationships
108
+
109
+ | Tool | Description |
110
+ |---|---|
111
+ | `add_relationship` | Create a relationship between two issues |
112
+
113
+ ### Monitors
114
+
115
+ | Tool | Description |
116
+ |---|---|
117
+ | `add_monitor` | Add yourself as a monitor of an issue |
118
+
119
+ ### Tags
120
+
121
+ | Tool | Description |
122
+ |---|---|
123
+ | `list_tags` | List all available tags |
124
+ | `attach_tags` | Attach tags to an issue |
125
+ | `detach_tag` | Remove a tag from an issue |
126
+
127
+ ### Projects
128
+
129
+ | Tool | Description |
130
+ |---|---|
131
+ | `list_projects` | List all accessible projects |
132
+ | `get_project_versions` | Get versions of a project |
133
+ | `get_project_categories` | Get categories of a project |
134
+ | `get_project_users` | Get users of a project |
135
+
136
+ ### Metadata & system
137
+
138
+ | Tool | Description |
139
+ |---|---|
140
+ | `get_metadata` | Retrieve cached metadata (projects, users, versions, categories) |
141
+ | `sync_metadata` | Refresh the metadata cache |
142
+ | `list_filters` | List saved filters |
143
+ | `get_current_user` | Retrieve your own user profile |
144
+ | `list_languages` | List available languages |
145
+ | `get_config` | Show server configuration (base URL, cache TTL) |
146
+ | `get_mantis_version` | Get MantisBT version and check for updates |
147
+
148
+ ## HTTP mode
149
+
150
+ For use as a standalone server (e.g. in remote setups):
151
+
152
+ ```bash
153
+ MANTIS_BASE_URL=... MANTIS_API_KEY=... TRANSPORT=http PORT=3456 node dist/index.js
154
+ ```
155
+
156
+ Health check: `GET http://localhost:3456/health`
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ npm install # Install dependencies
162
+ npm run build # Compile TypeScript → dist/
163
+ npm run typecheck # Type check without output
164
+ npm run dev # Watch mode for development
165
+ npm test # Run tests (vitest)
166
+ npm run test:watch # Run tests in watch mode
167
+ npm run test:coverage # Coverage report
168
+ ```
169
+
170
+ ## License
171
+
172
+ MIT – see [LICENSE](LICENSE)
173
+
174
+ ## Contributing
175
+
176
+ Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md).
177
+ Repository: [codeberg.org/dpesch/mantisbt-mcp-server](https://codeberg.org/dpesch/mantisbt-mcp-server)
package/dist/cache.js ADDED
@@ -0,0 +1,52 @@
1
+ import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ // ---------------------------------------------------------------------------
4
+ // MetadataCache
5
+ // ---------------------------------------------------------------------------
6
+ export class MetadataCache {
7
+ filePath;
8
+ ttlSeconds;
9
+ cacheDir;
10
+ constructor(cacheDir, ttlSeconds) {
11
+ this.cacheDir = cacheDir;
12
+ this.filePath = join(cacheDir, 'metadata.json');
13
+ this.ttlSeconds = ttlSeconds;
14
+ }
15
+ async isValid() {
16
+ try {
17
+ const raw = await readFile(this.filePath, 'utf-8');
18
+ const file = JSON.parse(raw);
19
+ const ageSeconds = (Date.now() - file.timestamp) / 1000;
20
+ return ageSeconds < this.ttlSeconds;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ async load() {
27
+ try {
28
+ const raw = await readFile(this.filePath, 'utf-8');
29
+ const file = JSON.parse(raw);
30
+ return file.data;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ async save(data) {
37
+ await mkdir(this.cacheDir, { recursive: true });
38
+ const file = {
39
+ timestamp: Date.now(),
40
+ data,
41
+ };
42
+ await writeFile(this.filePath, JSON.stringify(file, null, 2), 'utf-8');
43
+ }
44
+ async invalidate() {
45
+ try {
46
+ await unlink(this.filePath);
47
+ }
48
+ catch {
49
+ // Already gone — that is fine
50
+ }
51
+ }
52
+ }
package/dist/client.js ADDED
@@ -0,0 +1,114 @@
1
+ // ---------------------------------------------------------------------------
2
+ // MantisApiError
3
+ // ---------------------------------------------------------------------------
4
+ export class MantisApiError extends Error {
5
+ statusCode;
6
+ constructor(statusCode, message) {
7
+ super(`MantisBT API error ${statusCode}: ${message}`);
8
+ this.statusCode = statusCode;
9
+ this.name = 'MantisApiError';
10
+ }
11
+ }
12
+ export class MantisClient {
13
+ baseUrl;
14
+ apiKey;
15
+ responseObserver;
16
+ constructor(baseUrl, apiKey, responseObserver) {
17
+ // Ensure base URL ends without trailing slash; we always append /api/rest/
18
+ this.baseUrl = baseUrl.replace(/\/$/, '');
19
+ this.apiKey = apiKey;
20
+ this.responseObserver = responseObserver;
21
+ }
22
+ // ---------------------------------------------------------------------------
23
+ // Private helpers
24
+ // ---------------------------------------------------------------------------
25
+ buildUrl(path, params) {
26
+ const url = new URL(`${this.baseUrl}/api/rest/${path}`);
27
+ if (params) {
28
+ for (const [key, value] of Object.entries(params)) {
29
+ if (value !== undefined) {
30
+ url.searchParams.set(key, String(value));
31
+ }
32
+ }
33
+ }
34
+ return url.toString();
35
+ }
36
+ headers() {
37
+ return {
38
+ 'Authorization': this.apiKey,
39
+ 'Content-Type': 'application/json',
40
+ 'Accept': 'application/json',
41
+ };
42
+ }
43
+ async handleResponse(response) {
44
+ if (response.ok) {
45
+ this.responseObserver?.(response);
46
+ // Some DELETE endpoints return 204 No Content
47
+ if (response.status === 204) {
48
+ return undefined;
49
+ }
50
+ const text = await response.text();
51
+ if (!text)
52
+ return undefined;
53
+ return JSON.parse(text);
54
+ }
55
+ let message = response.statusText;
56
+ try {
57
+ const body = await response.text();
58
+ if (body) {
59
+ const parsed = JSON.parse(body);
60
+ if (parsed.message)
61
+ message = parsed.message;
62
+ else
63
+ message = body;
64
+ }
65
+ }
66
+ catch {
67
+ // ignore parse errors — keep statusText as message
68
+ }
69
+ throw new MantisApiError(response.status, message);
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // Public API methods
73
+ // ---------------------------------------------------------------------------
74
+ async get(path, params) {
75
+ const response = await fetch(this.buildUrl(path, params), {
76
+ method: 'GET',
77
+ headers: this.headers(),
78
+ });
79
+ return this.handleResponse(response);
80
+ }
81
+ async post(path, body) {
82
+ const response = await fetch(this.buildUrl(path), {
83
+ method: 'POST',
84
+ headers: this.headers(),
85
+ body: JSON.stringify(body),
86
+ });
87
+ return this.handleResponse(response);
88
+ }
89
+ async patch(path, body) {
90
+ const response = await fetch(this.buildUrl(path), {
91
+ method: 'PATCH',
92
+ headers: this.headers(),
93
+ body: JSON.stringify(body),
94
+ });
95
+ return this.handleResponse(response);
96
+ }
97
+ async delete(path) {
98
+ const response = await fetch(this.buildUrl(path), {
99
+ method: 'DELETE',
100
+ headers: this.headers(),
101
+ });
102
+ return this.handleResponse(response);
103
+ }
104
+ async getVersion() {
105
+ const response = await fetch(this.buildUrl('users/me'), {
106
+ method: 'GET',
107
+ headers: this.headers(),
108
+ });
109
+ if (!response.ok) {
110
+ throw new MantisApiError(response.status, response.statusText);
111
+ }
112
+ return response.headers.get('X-Mantis-Version') ?? 'unknown';
113
+ }
114
+ }
package/dist/config.js ADDED
@@ -0,0 +1,54 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ // ---------------------------------------------------------------------------
5
+ // Loader
6
+ // ---------------------------------------------------------------------------
7
+ async function readMantisJson() {
8
+ const filePath = join(homedir(), '.claude', 'mantis.json');
9
+ try {
10
+ const raw = await readFile(filePath, 'utf-8');
11
+ return JSON.parse(raw);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ let cachedConfig = null;
18
+ export async function getConfig() {
19
+ if (cachedConfig)
20
+ return cachedConfig;
21
+ let baseUrl = process.env.MANTIS_BASE_URL ?? '';
22
+ let apiKey = process.env.MANTIS_API_KEY ?? '';
23
+ // If env vars are missing, try ~/.claude/mantis.json as fallback
24
+ if (!baseUrl || !apiKey) {
25
+ const json = await readMantisJson();
26
+ if (json) {
27
+ if (!baseUrl && json.base_url)
28
+ baseUrl = json.base_url;
29
+ if (!apiKey && json.api_key)
30
+ apiKey = json.api_key;
31
+ }
32
+ }
33
+ const missing = [];
34
+ if (!baseUrl)
35
+ missing.push('MANTIS_BASE_URL');
36
+ if (!apiKey)
37
+ missing.push('MANTIS_API_KEY');
38
+ if (missing.length > 0) {
39
+ throw new Error(`Missing required MantisBT configuration: ${missing.join(', ')}.\n` +
40
+ `Set the environment variables or provide ~/.claude/mantis.json with keys "base_url" and "api_key".`);
41
+ }
42
+ const defaultCacheDir = join(homedir(), '.cache', 'mantisbt-mcp');
43
+ const cacheDir = process.env.MANTIS_CACHE_DIR ?? defaultCacheDir;
44
+ const cacheTtl = process.env.MANTIS_CACHE_TTL
45
+ ? parseInt(process.env.MANTIS_CACHE_TTL, 10)
46
+ : 3600;
47
+ cachedConfig = {
48
+ baseUrl: baseUrl.replace(/\/$/, ''), // strip trailing slash
49
+ apiKey,
50
+ cacheDir,
51
+ cacheTtl,
52
+ };
53
+ return cachedConfig;
54
+ }
@@ -0,0 +1,23 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Relationship type IDs
3
+ // ---------------------------------------------------------------------------
4
+ // Note: the MantisBT REST API only accepts numeric type IDs, not string names.
5
+ export const RELATIONSHIP_TYPES = {
6
+ DUPLICATE_OF: 0,
7
+ RELATED_TO: 1,
8
+ PARENT_OF: 2, // "depends on" — this issue depends on the target
9
+ CHILD_OF: 3, // "blocks" — this issue blocks the target
10
+ HAS_DUPLICATE: 4,
11
+ };
12
+ // ---------------------------------------------------------------------------
13
+ // Status names (internal English names used in API calls)
14
+ // ---------------------------------------------------------------------------
15
+ export const STATUS_NAMES = [
16
+ 'new',
17
+ 'feedback',
18
+ 'acknowledged',
19
+ 'confirmed',
20
+ 'assigned',
21
+ 'resolved',
22
+ 'closed',
23
+ ];