# MantisBT MCP Server [![npm version](https://img.shields.io/npm/v/@dpesch/mantisbt-mcp-server)](https://www.npmjs.com/package/@dpesch/mantisbt-mcp-server) [![license](https://img.shields.io/npm/l/@dpesch/mantisbt-mcp-server)](LICENSE) [![MCP compatible](https://img.shields.io/badge/MCP-compatible-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![MCP Badge](https://lobehub.com/badge/mcp/dpesch-mantisbt-mcp-server)](https://lobehub.com/mcp/dpesch-mantisbt-mcp-server) [![MantisBT MCP Server](https://glama.ai/mcp/servers/dpesch/mantisbt-mcp-server/badges/card.svg)](https://glama.ai/mcp/servers/dpesch/mantisbt-mcp-server) **English** · [Deutsch](README.de.md) 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. ## Requirements - Node.js ≥ 22 - MantisBT installation with REST API enabled (version 2.23+) - MantisBT API token (create under *My Account → API Tokens*) ## Installation **Via npx (recommended):** Add to `~/.claude/claude_desktop_config.json` (Claude Desktop) or your local `claude_desktop_config.json` (Claude Code): ```json { "mcpServers": { "mantisbt": { "command": "npx", "args": ["-y", "@dpesch/mantisbt-mcp-server"], "env": { "MANTIS_BASE_URL": "https://your-mantis.example.com/api/rest", "MANTIS_API_KEY": "your-api-token" } } } } ``` **Local build:** ```bash git clone https://codeberg.org/dpesch/mantisbt-mcp-server cd mantisbt-mcp-server npm run init npm run build ``` ```json { "mcpServers": { "mantisbt": { "command": "node", "args": ["/path/to/mantisbt-mcp-server/dist/index.js"], "env": { "MANTIS_BASE_URL": "https://your-mantis.example.com/api/rest", "MANTIS_API_KEY": "your-api-token" } } } } ``` ## Configuration ### Environment variables | Variable | Required | Default | Description | |---|---|---|---| | `MANTIS_BASE_URL` | ✅ | – | Base URL of your MantisBT installation. Both `https://your-mantis.example.com` and `https://your-mantis.example.com/api/rest` are accepted — the `/api/rest` suffix is normalized automatically. | | `MANTIS_API_KEY` | ✅ | – | API token for authentication | | `MANTIS_USE_INDEX_PHP` | – | auto | Set to `true` when URL rewriting is unavailable — REST requests then use `/api/rest/index.php/` instead of `/api/rest/`. Detected automatically when `MANTIS_BASE_URL` ends with `/api/rest/index.php`; an explicit value always wins. See the [cookbook](docs/cookbook.md#connect-to-an-installation-without-url-rewriting). | | `MANTIS_CACHE_DIR` | – | `~/.cache/mantisbt-mcp` | Directory for the metadata cache | | `MANTIS_CACHE_TTL` | – | `3600` | Cache lifetime in seconds | | `TRANSPORT` | – | `stdio` | Transport mode: `stdio` or `http` | | `PORT` | – | `3000` | Port for HTTP mode | | `MCP_HTTP_HOST` | – | `127.0.0.1` | Bind address for HTTP mode. **Changed from `0.0.0.0` to `127.0.0.1`** — the server now listens on localhost only by default. Set to `0.0.0.0` for Docker or remote access. | | `MCP_HTTP_TOKEN` | ✅ (HTTP mode) | – | Bearer token for the `/mcp` endpoint (`Authorization: Bearer `). **Required when `TRANSPORT=http`** — the server refuses to start in HTTP mode without it, so tools are never exposed unauthenticated. Ignored in stdio mode. The `/health` endpoint is always public. | | `MANTIS_SEARCH_ENABLED` | – | `false` | Set to `true` to enable semantic search | | `MANTIS_SEARCH_BACKEND` | – | `vectra` | Vector store backend: `vectra` (pure JS) or `sqlite-vec` (requires manual install) | | `MANTIS_SEARCH_DIR` | – | `{MANTIS_CACHE_DIR}/search` | Directory for the search index | | `MANTIS_SEARCH_MODEL` | – | `Xenova/paraphrase-multilingual-MiniLM-L12-v2` | Embedding model name (downloaded once on first use, ~80 MB) | | `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. | | `MANTIS_UPLOAD_DIR` | – | – | Restrict `upload_file`'s `file_path` to files within this directory (path traversal via `../` is blocked). In **stdio mode** `file_path` is unrestricted unless this is set. In **HTTP mode** `file_path` reads from the server's filesystem, so it is **disabled unless this variable is set** — HTTP clients should upload via the `content` (Base64) parameter instead. | ## Available tools ### Issues | Tool | Description | |---|---| | `get_issue` | Retrieve an issue by its numeric ID; optional `select` for field projection to reduce response size | | `get_issues` | Retrieve multiple issues by ID in one call (1–50 IDs); missing or inaccessible IDs return `null` at their position instead of failing the call | | `list_issues` | Filter issues by project, status, author, and more; optional `select` for field projection and `status` for client-side status filtering — canonical English status names (e.g. `"new"`, `"resolved"`) are matched by ID, making the filter language-independent on localized installations | | `create_issue` | Create a new issue; `severity` and `priority` must be canonical English names (e.g. `minor`, `major`, `normal`, `high`) — call `get_issue_enums` to see all valid values and their localized labels; optional `handler` parameter accepts a username as alternative to `handler_id` (resolved against project members); optional `custom_fields` to set custom field values | | `update_issue` | Update an existing issue; enum fields (`status`, `priority`, `severity`, `resolution`, `reproducibility`) accept canonical English names, localized names, or numeric IDs — the server resolves names to IDs automatically; supports `custom_fields` and an optional `note` parameter that appends a note in the same call (e.g. the reason for a status change) | | `delete_issue` | Delete an issue | ### Notes | Tool | Description | |---|---| | `list_notes` | List all notes of an issue | | `add_note` | Add a note to an issue | | `delete_note` | Delete a note | ### Attachments | Tool | Description | |---|---| | `list_issue_files` | List attachments of an issue | | `upload_file` | Upload a file to an issue — preferred: local `file_path` (server reads and encodes automatically); fallback: Base64-encoded `content` + `filename` (use only when `file_path` is not available) | ### Relationships | Tool | Description | |---|---| | `add_relationship` | Create a relationship between two issues; optional `type_name` parameter accepts a string name (e.g. `"related_to"`, `"duplicate_of"`) as alternative to numeric `type_id` | | `remove_relationship` | Remove a relationship from an issue (use the `id` from the relationship object, not the type) | ### Monitors | Tool | Description | |---|---| | `add_monitor` | Add a user as a monitor of an issue | | `remove_monitor` | Remove a user as a monitor of an issue | ### Tags | Tool | Description | |---|---| | `list_tags` | List all available tags; falls back to the metadata cache when `GET /tags` returns 404 (run `sync_metadata` first to populate) | | `attach_tags` | Attach tags to an issue | | `detach_tag` | Remove a tag from an issue | ### Projects | Tool | Description | |---|---| | `list_projects` | List all accessible projects; returns normalized project data (consistent with `sync_metadata` cache) | | `get_project_versions` | Get versions of a project; optional `obsolete` and `inherit` booleans to include obsolete or parent-inherited versions; each version includes a `timestamp` field (version date); see `create_version`, `update_version`, `release_version`, `delete_version` to modify versions | | `create_version` | Create a new version in a project; returns the created version object (id, name, description, released, obsolete, timestamp); requires `manage_project_threshold` (default: manager) | | `update_version` | Update an existing version; only the fields you pass are changed (at least one required); renaming rewrites version/target_version/fixed_in_version on all referencing issues; requires `manage_project_threshold` (default: manager) | | `release_version` | Mark a version as released and set its date (default: now); optionally create a follow-up version in the same step via `next_version`; requires `manage_project_threshold` (default: manager) | | `delete_version` | Permanently delete a version — irreversible, MantisBT clears version/target_version/fixed_in_version on all referencing issues; prefer `update_version` with `obsolete=true` as the non-destructive alternative; requires `manage_project_threshold` (default: manager) | | `get_project_categories` | Get categories of a project | | `get_project_users` | Get users of a project | | `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 | ### Semantic search *(optional)* Instead of exact keyword matching, semantic search understands the *meaning* behind a query. Ask in plain language — the search engine finds conceptually related issues even when the wording doesn't match: - *"login fails after password reset"* — finds issues about authentication edge cases - *"performance problems on the checkout page"* — surfaces related reports regardless of the exact terminology used - *"duplicate entries in the invoice list"* — catches issues described as "shown twice", "double records", etc. 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). Activate with `MANTIS_SEARCH_ENABLED=true`. | Tool | Description | |---|---| | `search_issues` | Natural language search over all indexed issues — returns top-N results with cosine similarity score; 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**`) | | `rebuild_search_index` | Build or update the search index; `full: true` clears and rebuilds from scratch | | `get_search_index_status` | Return the current fill level of the search index: how many issues are indexed vs. total, and the timestamp of the last sync | #### Which backend to choose? | | `vectra` *(default)* | `sqlite-vec` | |---|---|---| | Dependencies | None (pure JS) | Requires native build tools | | Install | Included | `npm install sqlite-vec better-sqlite3` | | Best for | Up to ~10,000 issues | 10,000+ issues | | Performance | Fast enough for most setups | Faster for large corpora | Start with `vectra`. Switch to `sqlite-vec` if indexing or query times become noticeably slow. ```bash npm install sqlite-vec better-sqlite3 # then set MANTIS_SEARCH_BACKEND=sqlite-vec ``` ### Metadata & system | Tool | Description | |---|---| | `get_issue_fields` | Return all field names valid for the `select` parameter of `list_issues` | | `get_metadata` | Retrieve a compact metadata summary: project/tag counts and per-project user/version/category counts; use `get_metadata_full` for complete arrays | | `get_metadata_full` | Return the full raw metadata cache as minified JSON (all projects with complete fields, users/versions/categories per project, all tags) | | `sync_metadata` | Refresh the metadata cache | | `list_filters` | List saved filters | | `get_current_user` | Retrieve your own user profile | | `list_languages` | List available languages | | `get_config` | Show server configuration (base URL, cache TTL) | | `get_issue_enums` | Return valid ID/name pairs for all issue enum fields (severity, status, priority, resolution, reproducibility) — use before `create_issue` / `update_issue` to look up correct values; on localized installations each entry may include a `canonical_name` with the standard English API name | | `get_mantis_version` | Get MantisBT version and check for updates | | `get_mcp_version` | Return the version of this mantisbt-mcp-server instance | ## Available resources MCP Resources are URI-addressable, read-only data that clients can fetch directly without calling a tool. They are the third MCP primitive alongside Tools and Prompts. Note that Resource support is less widely implemented in MCP clients than Tools — check your client's documentation. | Resource URI | Description | |---|---| | `mantis://me` | Profile of the authenticated API user (live fetch) | | `mantis://projects` | All accessible MantisBT projects as a compact list (cache-backed, refreshed via `sync_metadata`) | | `mantis://projects/{id}` | Combined project view: project fields + users + versions + categories in one call; cache-first, list-support for enumerating all available project URIs | | `mantis://enums` | Valid values for all issue enum fields: severity, priority, status, resolution, reproducibility (live fetch) | ## Available prompts MCP prompt templates are conversation starters that instruct the LLM to collect structured input and then call the appropriate tool. They are not tools themselves — they initiate a guided workflow. | Prompt | Required args | Optional args | Description | |---|---|---|---| | `create-bug-report` | `project_id`, `category`, `summary`, `description` | `steps_to_reproduce`, `expected`, `actual`, `environment` | Guides through a structured bug report and calls `create_issue` | | `create-feature-request` | `project_id`, `category`, `summary`, `description` | `use_case` | Guides through a feature request and calls `create_issue` | | `summarize-issue` | `issue_id` | – | Fetches an issue via `get_issue` and returns a concise summary | | `project-status` | `project_id` | – | Lists issues via `list_issues` and generates a status report grouped by severity | ## HTTP mode For use as a standalone server (e.g. in remote setups). `MCP_HTTP_TOKEN` is **required** in HTTP mode — the server refuses to start without it: ```bash MCP_HTTP_TOKEN=secret MANTIS_BASE_URL=... MANTIS_API_KEY=... \ TRANSPORT=http PORT=3456 node dist/index.js # With explicit bind address (required for Docker/remote): # MCP_HTTP_TOKEN=secret MANTIS_BASE_URL=... MANTIS_API_KEY=... \ # TRANSPORT=http PORT=3456 MCP_HTTP_HOST=0.0.0.0 node dist/index.js ``` Every `/mcp` request must send `Authorization: Bearer `. Over HTTP, `upload_file`'s `file_path` is disabled unless `MANTIS_UPLOAD_DIR` is set — use the `content` (Base64) parameter instead. Health check: `GET http://localhost:3456/health` (always public, no token required) ## Documentation - [**Cookbook**](docs/cookbook.md) — tool-oriented recipes with copy-paste-ready parameter examples for all registered tools - [**Usage Examples**](docs/examples.md) — natural language prompt examples for everyday use cases (no tool names required) ## Development ```bash npm run init # First-time setup: install deps, git hooks, typecheck npm run build # Compile TypeScript → dist/ npm run typecheck # Type check without output npm run dev # Watch mode for development npm test # Run tests (vitest) npm run test:watch # Run tests in watch mode npm run test:coverage # Coverage report ``` ## License MIT – see [LICENSE](LICENSE) ## Contributing Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md). Repository: [codeberg.org/dpesch/mantisbt-mcp-server](https://codeberg.org/dpesch/mantisbt-mcp-server)