{
  "markdown": "# Redmine MCP Server\n\n[![PyPI Version](https://img.shields.io/pypi/v/redmine-mcp-server.svg)](https://pypi.org/project/redmine-mcp-server/)\n[![License](https://img.shields.io/github/license/jztan/redmine-mcp-server.svg)](LICENSE)\n[![Python Version](https://img.shields.io/pypi/pyversions/redmine-mcp-server.svg)](https://pypi.org/project/redmine-mcp-server/)\n[![Redmine Version](https://img.shields.io/badge/Redmine-6.1%20%7C%207.0-blue.svg)](#redmine-compatibility)\n[![GitHub Issues](https://img.shields.io/github/issues/jztan/redmine-mcp-server.svg)](https://github.com/jztan/redmine-mcp-server/issues)\n[![CI](https://github.com/jztan/redmine-mcp-server/actions/workflows/pr-tests.yml/badge.svg)](https://github.com/jztan/redmine-mcp-server/actions/workflows/pr-tests.yml)\n[![Coverage](https://codecov.io/gh/jztan/redmine-mcp-server/branch/master/graph/badge.svg)](https://codecov.io/gh/jztan/redmine-mcp-server)\n[![Downloads](https://pepy.tech/badge/redmine-mcp-server)](https://pepy.tech/project/redmine-mcp-server)\n\nA Model Context Protocol (MCP) server that connects AI assistants to Redmine. It exposes your Redmine instance's projects, issues, time tracking, wiki pages, and files as MCP tools.\n\n**mcp-name: io.github.jztan/redmine-mcp-server**\n\n<p align=\"center\">\n  <a href=\"https://redmine-mcp-server.jztan.com\">\n    <img src=\"https://raw.githubusercontent.com/jztan/redmine-mcp-server/develop/assets/redmine-mcp-demo.gif\" alt=\"An AI agent triaging a Redmine sprint backlog through redmine-mcp-server\" width=\"820\" />\n  </a>\n</p>\n\n<p align=\"center\"><sub>An AI agent triaging a Redmine sprint through redmine-mcp-server. <a href=\"https://redmine-mcp-server.jztan.com\">Try the live demo →</a></sub></p>\n\n## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./docs/contributing.md) | [Troubleshooting](./docs/troubleshooting.md)\n\n## Features\n\n- **45 MCP tools on a stock Redmine, 58 with the RedmineUP and DMSF plugins** (plus 1 operator tool gated by `REDMINE_MCP_EXPOSE_ADMIN_TOOLS=true`): Issues, projects, time tracking, wiki, Gantt, file operations, membership management, products, contacts and deals (CRM), DMSF documents, and more\n- **Interactive Kanban Board**: `show_triage_board` renders a live, drag-and-drop issue board right in the chat via the MCP Apps extension\n- **Flexible Authentication**: API key, username/password, or OAuth2 per-user tokens\n- **Prompt Injection Protection**: User-controlled content wrapped in boundary tags for safe LLM consumption\n- **Read-Only Mode**: Restrict to read-only operations via `REDMINE_MCP_READ_ONLY` environment variable\n- **HTTP File Serving**: Secure attachment access via UUID-based URLs with automatic expiry\n- **Pagination Support**: Handle large result sets with configurable limits\n- **MCP Compliant**: Built on FastMCP with HTTP transport\n- **Docker Ready**: Dockerfile, docker-compose setup, and prebuilt images on GHCR\n\n## Quick Start\n\n1. **Install the package**\n   ```bash\n   pip install redmine-mcp-server\n   ```\n2. **Create a `.env` file** with your Redmine credentials (see [Installation](#installation) for template)\n3. **Start the server**\n   ```bash\n   redmine-mcp-server\n   ```\n4. **Add the server to your MCP client** using one of the guides in [MCP Client Configuration](#mcp-client-configuration).\n\nOnce running, the server listens on `http://localhost:8000` with the MCP endpoint at `/mcp`, health check at `/health`, and file serving at `/files/{file_id}`.\n\n## Installation\n\n### Prerequisites\n\n- Python 3.10+ (for local installation)\n- Docker (alternative deployment, uses Python 3.13)\n- Access to a Redmine instance\n\n#### Redmine Compatibility\n\nThe integration suite passes in full against Redmine 6.1 and 7.0. Older\nversions are untested. Individual tools list their own minimum where one is\nknown (global search needs 3.3.0+, issue watchers 2.3.0+, project time-entry\nactivities 3.4.0+), so on an older server those specific tools fail rather than\nthe whole server.\n\nOAuth2 is the one hard requirement: it needs Redmine 6.1+ for Doorkeeper\nsupport. See [docs/oauth-setup.md](docs/oauth-setup.md).\n\n### Install from PyPI (Recommended)\n\n```bash\n# Install the package\npip install redmine-mcp-server\n\n# Create configuration file .env\ncat > .env << 'EOF'\n# Redmine connection (required)\nREDMINE_URL=https://your-redmine-server.com\n\n# Authentication - Use either API key (recommended) or username/password\nREDMINE_API_KEY=your_api_key\n# OR use username/password:\n# REDMINE_USERNAME=your_username\n# REDMINE_PASSWORD=your_password\n\n# Server configuration (optional, defaults shown)\nSERVER_HOST=0.0.0.0\nSERVER_PORT=8000\n\n# Public URL for file serving (optional)\nPUBLIC_HOST=localhost\nPUBLIC_PORT=8000\n\n# File management (optional)\nATTACHMENTS_DIR=./attachments\nAUTO_CLEANUP_ENABLED=true\nCLEANUP_INTERVAL_MINUTES=10\nATTACHMENT_EXPIRES_MINUTES=60\nEOF\n\n# Edit .env with your actual Redmine settings\nnano .env  # or use your preferred editor\n\n# Run the server\nredmine-mcp-server\n# Or alternatively:\npython -m redmine_mcp_server.main\n```\n\nThe server runs on `http://localhost:8000` with the MCP endpoint at `/mcp`, health check at `/health`, and file serving at `/files/{file_id}`.\n\n### Environment Variables Configuration\n\n<details>\n<summary><strong>Environment Variables</strong></summary>\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `REDMINE_URL` | Yes | – | Base URL of your Redmine instance |\n| `REDMINE_AUTH_MODE` | No | `legacy` | Authentication mode: `legacy`, `legacy-per-user`, `oauth`, or `oauth-proxy` (see [Authentication](#authentication)) |\n| `REDMINE_PER_USER_TRUST_PROXY` | Yes* | `false` | Required for `legacy-per-user` mode. Operator attestation: \"this server sits behind TLS and my proxy does not forward client `X-Forwarded-Proto`.\" |\n| `REDMINE_PER_USER_AUDIT_IDENTITY` | No | `false` | `legacy-per-user` only: resolve and log the Redmine user ID per request (adds one extra round-trip) |\n| `REDMINE_API_KEY` | Yes† | – | API key (legacy mode only) |\n| `REDMINE_USERNAME` | Yes† | – | Username for basic auth (legacy mode only) |\n| `REDMINE_PASSWORD` | Yes† | – | Password for basic auth (legacy mode only) |\n| `REDMINE_MCP_BASE_URL` | Yes‡ | `http://localhost:3040` | Public base URL of this server, no trailing slash (OAuth modes only) |\n| `FASTMCP_STREAMABLE_HTTP_PATH` | No | `/mcp` | MCP transport path inside `REDMINE_MCP_BASE_URL` |\n| `REDMINE_INTROSPECT_CLIENT_ID` | Yes‡ | – | Doorkeeper OAuth client ID used by the MCP server to introspect Bearer tokens (RFC 7662). Register a confidential OAuth app in Redmine (see [`docs/oauth-setup.md`](docs/oauth-setup.md) Step 2). |\n| `REDMINE_INTROSPECT_CLIENT_SECRET` | Yes‡ | – | Secret for the introspection client |\n| `REDMINE_MCP_JWT_SIGNING_KEY` | Yes§ | – | Stable signing/encryption key used by FastMCP OAuthProxy tokens and storage |\n| `REDMINE_OAUTH_CLIENT_ID` | No | – | Optional upstream Redmine OAuth client ID for `oauth-proxy`; defaults to `REDMINE_INTROSPECT_CLIENT_ID` |\n| `REDMINE_OAUTH_CLIENT_SECRET` | No | – | Optional upstream Redmine OAuth client secret for `oauth-proxy`; defaults to `REDMINE_INTROSPECT_CLIENT_SECRET` |\n| `FASTMCP_HOME` | No | platform default (`/app/data/fastmcp` in Docker) | FastMCP data directory. In `oauth-proxy` mode, encrypted OAuthProxy state is stored below `FASTMCP_HOME/oauth-proxy/`, and must be on a persistent volume to survive a container rebuild |\n| `REDMINE_MCP_ALLOWED_CLIENT_REDIRECT_URIS` | No | loopback only | `oauth-proxy` client redirect-URI allowlist (glob patterns, comma/space separated). Unset = `http://localhost:*` and `http://127.0.0.1:*`; `*` = allow any |\n| `HEALTH_INTROSPECTION_TTL_SECONDS` | No | `30` | TTL (seconds) for the `/health` Doorkeeper introspection probe cache. Set to `0` to disable caching. |\n| `SERVER_HOST` | No | `0.0.0.0` | Host/IP the MCP server binds to |\n| `SERVER_PORT` | No | `8000` | Port the MCP server listens on |\n| `PUBLIC_HOST` | No | `localhost` | Hostname used when generating download URLs |\n| `PUBLIC_PORT` | No | `8000` | Public port used for download URLs |\n| `PUBLIC_SCHEME` | No | derived | Scheme for download URLs (`http` or `https`). When unset, `https` is derived from `PUBLIC_PORT=443`, otherwise `http`. Set explicitly for TLS on a nonstandard port. Default ports (80/443) are omitted from generated URLs. |\n| `REDMINE_PUBLIC_URL` | No | – | Publicly-reachable URL of your Redmine instance. When set, `content_url` values returned on attachments are rewritten from `REDMINE_URL`'s origin to this one (preserving path/query/fragment and any reverse-proxy subpath). Useful when `REDMINE_URL` is the internal container hostname unreachable from MCP clients. When unset, the raw URL Redmine echoes back is returned. |\n| `ATTACHMENTS_DIR` | No | `./attachments` | Directory for downloaded attachments |\n| `ATTACHMENT_MAX_DOWNLOAD_BYTES` | No | `209715200` (200 MB) | Cap applied to every `get_redmine_attachment` download regardless of content type. Exceeding the cap aborts the download mid-stream and deletes the partial file. |\n| `REDMINE_MCP_UPLOAD_FILE_ROOTS` | No | – | Extra directories allowed as `file_path` upload sources (OS path separator-separated). `ATTACHMENTS_DIR` is always allowed. Unset restricts uploads to `ATTACHMENTS_DIR` only. |\n| `AUTO_CLEANUP_ENABLED` | No | `true` | Toggle automatic cleanup of expired attachments |\n| `CLEANUP_INTERVAL_MINUTES` | No | `10` | Interval for cleanup task |\n| `ATTACHMENT_EXPIRES_MINUTES` | No | `60` | Expiry window for generated download URLs |\n| `REDMINE_MCP_EXPOSE_ADMIN_TOOLS` | No | `false` | Expose operator/admin tools on the MCP surface. Currently gates `cleanup_attachment_files`. The background cleanup task runs regardless of this flag. |\n| `REDMINE_SSL_VERIFY` | No | `true` | Enable/disable SSL certificate verification |\n| `REDMINE_SSL_CERT` | No | – | Path to custom CA certificate file |\n| `REDMINE_SSL_CLIENT_CERT` | No | – | Path to client certificate for mutual TLS |\n| `REDMINE_TIMEOUT` | No | `30` | Whole seconds to wait for a Redmine HTTP response before failing the call. Applied as a connect timeout of at most 10s plus a read timeout of the full value. Set to `0` to wait indefinitely, which restores the previous behavior and can hang the request. |\n| `REDMINE_MCP_READ_ONLY` | No | `false` | Block all write operations (create/update/delete) when set to `true` |\n| `REDMINE_MCP_ALLOW_TOOLS` | No | – | Expose only these tools (comma-separated names). Unset exposes all; set but naming no tool refuses to start. Narrows the surface only: a listed tool whose plugin flag is off stays hidden. Whole tools, so per-action control on `manage_X` remains `REDMINE_MCP_READ_ONLY`'s job. Names matching no tool are warned about at startup ([details](docs/tool-reference.md#tool-allow-list)) |\n| `REDMINE_MCP_ALLOW_TOOLS_FILE` | No | – | Path to a file with one allowed tool name per line (`#` starts a comment). Used when `REDMINE_MCP_ALLOW_TOOLS` is unset or empty |\n| `REDMINE_OAUTH_SCOPE_ENFORCEMENT` | No | `on` | OAuth modes only: deny tool calls whose access token lacks the tool's Redmine permission scopes, and filter `tools/list` accordingly. Set to `off` temporarily while re-consenting older tokens ([details](docs/oauth-setup.md#scope-enforcement)) |\n| `REDMINE_OAUTH_DISCOVERY_AS` | No | `redmine` | OAuth modes only: which authorization server discovery advertises. `redmine` names your Redmine; `self` advertises this server (issuer = `REDMINE_MCP_BASE_URL`) and serves RFC 8414 metadata at its own canonical well-known location, which clients that probe there need, Cursor among them ([details](docs/oauth-setup.md#cursor-and-self-as-discovery)) |\n| `REDMINE_MCP_SCOPES` | No | – | OAuth modes only: advertise a subset of scopes in discovery, matching the permissions your Redmine OAuth Application actually enables. Avoids `invalid_scope` at consent when a client requests the full advertised list |\n| `REDMINE_AGILE_ENABLED` | No | `false` | Enable RedmineUP Agile plugin support: `get_redmine_issue` returns `story_points`, `agile_sprint_id`, `agile_position`; `update_redmine_issue` accepts `story_points` |\n| `REDMINE_CHECKLISTS_ENABLED` | No | `false` | Enable RedmineUP Checklists plugin support: `get_checklist`, `create_checklist_item`, `update_checklist_item` (requires Checklists Pro plugin) |\n| `REDMINE_PRODUCTS_ENABLED` | No | `false` | Enable RedmineUP Products plugin support: `manage_product` (action=list/get/create/update) |\n| `REDMINE_CRM_ENABLED` | No | `false` | Enable RedmineUP CRM plugin support: `manage_contact` (action=list/get/create/update/delete/assign_to_project/remove_from_project) `list_contact_tags`, `manage_crm_note` (notes on contacts) and `list_crm_queries`. Requires the CRM plugin and the `view_contacts` / `view_private_contacts` permissions on the Redmine server, plus `add_contacts` / `edit_contacts` / `delete_contacts` for the write actions. In OAuth mode these are advertised as scopes only when this flag is set, so the OAuth application must grant them too. |\n| `REDMINE_CRM_EDITION` | No | `light` | Which build of the CRM plugin the Redmine server runs: `light` or `pro`. The two register different contact query filters — the Pro build registers the contact fields, the Light build registers only `tags` — and Redmine ignores an unregistered filter parameter without erroring, answering with the whole collection instead. So `manage_contact` refuses `first_name`, `last_name`, `middle_name`, `company`, `job_title`, `email`, `phone` and `author_id` on `list` unless this is `pro`, rather than returning a silently unfiltered list. The build cannot be detected: Redmine exposes plugin versions only through `admin/plugins`, which is HTML and admin-only. |\n| `REDMINE_DEALS_ENABLED` | No | `false` | Enable RedmineUP CRM **deals** support: `manage_deal` (action=list/get/create/update/delete), `list_deal_statuses`, `manage_deal_category`, `manage_crm_note` (notes on deals), `list_crm_queries` and, together with `REDMINE_PRODUCTS_ENABLED`, `add_deal_product`. Separate from `REDMINE_CRM_ENABLED` because the CRM plugin's Light edition ships no deals and defines none of the deal permissions, so advertising them there would make consent fail. Requires the CRM plugin's **Pro** edition, the `deals` project module enabled on the project, and the `view_deals` permission, plus `add_deals` / `edit_deals` / `delete_deals` for the write actions. |\n| `REDMINE_DMSF_ENABLED` | No | `false` | Enable DMSF document-management plugin support: `manage_document` (action=list/get/create/update). Requires `redmine_dmsf` plugin on the Redmine server. |\n| `REDMINE_TAGS_ENABLED` | No | `false` | Enable AlphaNodes additional_tags plugin support: `get_redmine_issue` returns a `tags` array, and `create_redmine_issue`/`update_redmine_issue` accept a `tag_list`. Requires the `additional_tags` plugin and the `view_issue_tags` / `create_issue_tags` / `edit_issue_tags` permissions on the Redmine server. |\n| `REDMINE_AUTOFILL_REQUIRED_CUSTOM_FIELDS` | No | `false` | Enable one retry for issue creation by filling missing required custom fields |\n| `REDMINE_REQUIRED_CUSTOM_FIELD_DEFAULTS` | No | `{}` | JSON object mapping required custom field names to fallback values used when creating issues |\n| `REDMINE_ALLOW_PRIVATE_FETCH_URLS` | No | `false` | **Warning:** disables all SSRF protection for attachment fetching. Never set to `true` in production. |\n\n*\\* Required when `REDMINE_AUTH_MODE=legacy-per-user`.*\n*† Required when `REDMINE_AUTH_MODE=legacy`. Either `REDMINE_API_KEY` or `REDMINE_USERNAME`+`REDMINE_PASSWORD` must be set. API key is recommended.*\n*‡ Required when `REDMINE_AUTH_MODE=oauth` or `REDMINE_AUTH_MODE=oauth-proxy`.*\n*§ Required when `REDMINE_AUTH_MODE=oauth-proxy`.*\nSecret values can also be supplied with Docker/Kubernetes-style file variables: `REDMINE_INTROSPECT_CLIENT_SECRET_FILE`, `REDMINE_MCP_JWT_SIGNING_KEY_FILE`, and `REDMINE_OAUTH_CLIENT_SECRET_FILE`.\n\nWhen `REDMINE_AUTOFILL_REQUIRED_CUSTOM_FIELDS=true`, `create_redmine_issue` retries once on relevant custom-field validation errors (for example `<Field Name> cannot be blank` or `<Field Name> is not included in the list`) and fills values only from:\n- the Redmine custom field `default_value`, or\n- `REDMINE_REQUIRED_CUSTOM_FIELD_DEFAULTS`\n\nIn practice only the second one can fire. The server reads project custom fields from `GET /projects/{id}.json?include=issue_custom_fields`, which Redmine renders as id and name only, so it never sees `default_value` -- see [`list_project_issue_custom_fields`](docs/tool-reference.md#list_project_issue_custom_fields). Set the env map if you want autofill to have anything to work with.\n\nExample:\n\n```bash\nREDMINE_AUTOFILL_REQUIRED_CUSTOM_FIELDS=true\nREDMINE_REQUIRED_CUSTOM_FIELD_DEFAULTS='{\"Required Field A\":\"Value A\",\"Required Field B\":\"Value B\"}'\n```\n\n</details>\n\n### SSL Certificate Configuration\n\nConfigure SSL certificate handling for Redmine servers with self-signed certificates or internal CA infrastructure.\n\n<details>\n<summary><strong>Self-Signed Certificates</strong></summary>\n\nIf your Redmine server uses a self-signed certificate or internal CA:\n\n```bash\n# In .env file\nREDMINE_URL=https://redmine.company.com\nREDMINE_API_KEY=your_api_key\nREDMINE_SSL_CERT=/path/to/ca-certificate.crt\n```\n\nSupported certificate formats: `.pem`, `.crt`, `.cer`\n\n</details>\n\n<details>\n<summary><strong>Mutual TLS (Client Certificates)</strong></summary>\n\nFor environments requiring client certificate authentication:\n\n```bash\n# In .env file\nREDMINE_URL=https://secure.redmine.com\nREDMINE_API_KEY=your_api_key\nREDMINE_SSL_CERT=/path/to/ca-bundle.pem\nREDMINE_SSL_CLIENT_CERT=/path/to/cert.pem,/path/to/key.pem\n```\n\n**Note**: Private keys must be unencrypted (Python requests library requirement).\n\n</details>\n\n<details>\n<summary><strong>Disable SSL Verification (Development Only)</strong></summary>\n\n⚠️ **WARNING**: Only use in development/testing environments!\n\n```bash\n# In .env file\nREDMINE_SSL_VERIFY=false\n```\n\nDisabling SSL verification makes your connection vulnerable to man-in-the-middle attacks.\n\n</details>\n\nFor SSL troubleshooting, see the [Troubleshooting Guide](./docs/troubleshooting.md#ssl-certificate-errors).\n\n## Authentication\n\nThe server supports four authentication modes, selected via `REDMINE_AUTH_MODE`. It defaults to `legacy`, so existing deployments keep working with no changes; OAuth2 support is purely additive.\n\n| Your situation | Mode | Redmine |\n|---|---|---|\n| Single shared credential, simplest setup | `legacy` (default) | any |\n| Multi-user, you control the MCP client | `oauth` | 6.1+ |\n| Hosted server, clients self-register (DCR) | `oauth-proxy` | 6.1+ |\n| Multi-user, Redmine too old for OAuth | `legacy-per-user` | < 6.1 |\n\nThe advanced modes are collapsed below. For full setup, the [OAuth2 Setup Guide](./docs/oauth-setup.md) covers `oauth` and `oauth-proxy`, and the [legacy-per-user guide](./docs/legacy-per-user-auth.md) covers `legacy-per-user`.\n\n### Legacy mode (default)\n\nA single shared credential (API key or username/password) configured once in `.env`. Every request to Redmine uses the same identity.\n\n```bash\nREDMINE_AUTH_MODE=legacy        # or omit entirely; this is the default\nREDMINE_URL=https://redmine.example.com\nREDMINE_API_KEY=your_api_key\n# OR:\n# REDMINE_USERNAME=your_username\n# REDMINE_PASSWORD=your_password\n```\n\n<details>\n<summary><strong>OAuth2 mode</strong> (multi-user, Redmine 6.1+)</summary>\n\nEach MCP request carries its own `Authorization: Bearer <token>`, so every user authenticates with their own Redmine account. The server validates each token against Doorkeeper's introspection endpoint before forwarding it, and exposes the OAuth2 discovery and `/revoke` endpoints clients need.\n\n```bash\nREDMINE_AUTH_MODE=oauth\nREDMINE_URL=https://redmine.example.com\nREDMINE_MCP_BASE_URL=https://redmine-mcp.example.com   # public URL of this server\n\n# Confidential OAuth app registered in Redmine admin (see setup guide)\nREDMINE_INTROSPECT_CLIENT_ID=...\nREDMINE_INTROSPECT_CLIENT_SECRET=...\n```\n\nYou register the OAuth app manually in Redmine admin → **Applications** (no Dynamic Client Registration). Full walkthrough, endpoint reference, and troubleshooting: [OAuth2 Setup Guide](./docs/oauth-setup.md).\n\n</details>\n\n<details>\n<summary><strong>OAuthProxy mode</strong> (hosted deployments with client self-registration)</summary>\n\nFastMCP acts as the MCP-facing authorization server: it handles DCR for MCP clients, then redirects users to Redmine as the upstream OAuth provider for consent. Use this when clients (e.g. Claude Desktop, VS Code) expect to register themselves.\n\n```bash\nREDMINE_AUTH_MODE=oauth-proxy\nREDMINE_URL=https://redmine.example.com\nREDMINE_MCP_BASE_URL=https://redmine-mcp.example.com   # public URL of this server\n\n# Confidential OAuth app registered in Redmine admin (see setup guide)\nREDMINE_INTROSPECT_CLIENT_ID=...\nREDMINE_INTROSPECT_CLIENT_SECRET=...\nREDMINE_MCP_JWT_SIGNING_KEY=...\n```\n\nThe upstream Redmine app must register `${REDMINE_MCP_BASE_URL}/auth/callback` as its redirect URI. Storage, scaling, and credential-reuse notes are in the [OAuth2 Setup Guide](./docs/oauth-setup.md).\n\n</details>\n\n<details>\n<summary><strong>legacy-per-user mode</strong> (Redmine older than 6.1)</summary>\n\nFor Redmine instances too old for OAuth, each user's MCP client sends its own Redmine API key in an `X-Redmine-API-Key` header. Each request runs as that user's identity with that user's permissions.\n\n**This is an advanced, opt-in mode.** It requires TLS end-to-end and a correctly configured reverse proxy. Read [`docs/legacy-per-user-auth.md`](docs/legacy-per-user-auth.md) for the threat model, firewall guidance, and revocation runbook before enabling it.\n\n**`mcp-remote` (recommended):**\n\n```json\n{ \"mcpServers\": { \"redmine\": {\n  \"command\": \"npx\",\n  \"args\": [\"mcp-remote\", \"https://your-host/mcp\",\n           \"--header\", \"X-Redmine-API-Key:${RM_KEY}\"],\n  \"env\": { \"RM_KEY\": \"<your redmine api key>\" }\n}}}\n```\n\nNote the colon with no surrounding spaces in `X-Redmine-API-Key:${RM_KEY}`. This avoids an arg-escaping bug in Cursor and Claude Desktop on Windows.\n\n**VS Code (`mcp.json`):**\n\nUse `.vscode/mcp.json` (workspace file) or the user profile `mcp.json`. The workspace `.mcp.json` silently drops `headers` (see microsoft/vscode#319528), so do not use that file. Pin VS Code 1.102 or newer.\n\n```json\n{\n  \"servers\": {\n    \"redmine\": {\n      \"type\": \"http\",\n      \"url\": \"https://your-host/mcp\",\n      \"headers\": { \"X-Redmine-API-Key\": \"${input:rmKey}\" },\n      \"inputs\": [{ \"id\": \"rmKey\", \"type\": \"promptString\",\n                   \"description\": \"Redmine API key\", \"password\": true }]\n    }\n  }\n}\n```\n\n**Unsupported:** any client that cannot set a custom request header, or that reserves the `Authorization` header for its own OAuth flow.\n\n</details>\n\n## MCP Client Configuration\n\nThe server exposes an HTTP endpoint at `http://127.0.0.1:8000/mcp`. Register it with your preferred MCP-compatible agent using the instructions below.\n\n> The examples below assume `legacy` or `oauth` mode. In `legacy-per-user` mode each client must also send an `X-Redmine-API-Key` header; see [legacy-per-user mode](#authentication) above for header-aware configs.\n\n<details>\n<summary><strong>Visual Studio Code (Native MCP Support)</strong></summary>\n\nVS Code has built-in MCP support via GitHub Copilot (requires VS Code 1.102+).\n\n**Using CLI (Quickest):**\n```bash\ncode --add-mcp '{\"name\":\"redmine\",\"type\":\"http\",\"url\":\"http://127.0.0.1:8000/mcp\"}'\n```\n\n**Using Command Palette:**\n1. Open Command Palette (`Cmd/Ctrl+Shift+P`)\n2. Run `MCP: Open User Configuration` (for global) or `MCP: Open Workspace Folder Configuration` (for project-specific)\n3. Add the configuration:\n   ```json\n   {\n     \"servers\": {\n       \"redmine\": {\n         \"type\": \"http\",\n         \"url\": \"http://127.0.0.1:8000/mcp\"\n       }\n     }\n   }\n   ```\n4. Save the file. VS Code will automatically load the MCP server.\n\n**Manual Configuration:**\nCreate `.vscode/mcp.json` in your workspace (or `mcp.json` in your user profile directory):\n```json\n{\n  \"servers\": {\n    \"redmine\": {\n      \"type\": \"http\",\n      \"url\": \"http://127.0.0.1:8000/mcp\"\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary><strong>Claude Code</strong></summary>\n\nAdd to Claude Code using the CLI command:\n\n```bash\nclaude mcp add --transport http redmine http://127.0.0.1:8000/mcp\n```\n\nOr configure manually in your Claude Code settings file (`~/.claude.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"redmine\": {\n      \"type\": \"http\",\n      \"url\": \"http://127.0.0.1:8000/mcp\"\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary><strong>Claude Desktop (macOS & Windows)</strong></summary>\n\nClaude Desktop's config file supports stdio transport only. Use FastMCP's proxy via `uv` to bridge to this HTTP server.\n\n**Setup:**\n1. Open Claude Desktop\n2. Click the **Claude** menu (macOS menu bar / Windows title bar) > **Settings...**\n3. Click the **Developer** tab > **Edit Config**\n4. Add the following configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"redmine\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"run\",\n        \"--with\", \"fastmcp\",\n        \"fastmcp\",\n        \"run\",\n        \"http://127.0.0.1:8000/mcp\"\n      ]\n    }\n  }\n}\n```\n\n5. Save the file, then **fully quit and restart** Claude Desktop\n6. Look for the tools icon in the input area to verify the connection\n\n**Config file locations:**\n- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n**Note:** The Redmine MCP server must be running before starting Claude Desktop.\n\n</details>\n\n<details>\n<summary><strong>Cursor</strong></summary>\n\nCursor talks to HTTP MCP servers directly, with no bridge.\n\n1. Create `~/.cursor/mcp.json` (available in every project) or `.cursor/mcp.json` in your project root (that project only):\n   ```json\n   {\n     \"mcpServers\": {\n       \"redmine\": {\n         \"url\": \"http://127.0.0.1:8000/mcp\"\n       }\n     }\n   }\n   ```\n2. Save the file. Cursor picks the server up automatically; its MCP settings list the server and the tools it loaded.\n\n**Note:** Cursor identifies a remote server by a bare `url` and has no `type` field, unlike the VS Code and Claude Code configs above.\n\n**In `legacy-per-user` mode**, add the API key header:\n\n```json\n{\n  \"mcpServers\": {\n    \"redmine\": {\n      \"url\": \"https://your-host/mcp\",\n      \"headers\": { \"X-Redmine-API-Key\": \"<your redmine api key>\" }\n    }\n  }\n}\n```\n\n**In `oauth` mode**, set `REDMINE_OAUTH_DISCOVERY_AS=self` on the MCP server. Cursor looks for authorization server metadata at its own canonical well-known location, which the default (`redmine`) discovery profile does not serve, so the flow stalls without it ([#188](https://github.com/jztan/redmine-mcp-server/issues/188)). See [Cursor and self-AS discovery](docs/oauth-setup.md#cursor-and-self-as-discovery).\n\n</details>\n\n<details>\n<summary><strong>Codex CLI</strong></summary>\n\nAdd to Codex CLI using the command:\n\n```bash\ncodex mcp add redmine -- npx -y mcp-client-http http://127.0.0.1:8000/mcp\n```\n\nOr configure manually in `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.redmine]\ncommand = \"npx\"\nargs = [\"-y\", \"mcp-client-http\", \"http://127.0.0.1:8000/mcp\"]\n```\n\n**Note:** Codex CLI primarily supports stdio-based MCP servers. The above uses `mcp-client-http` as a bridge for HTTP transport.\n\n</details>\n\n<details>\n<summary><strong>Kiro</strong></summary>\n\nKiro primarily supports stdio-based MCP servers. For HTTP servers, use an HTTP-to-stdio bridge:\n\n1. Create or edit `.kiro/settings/mcp.json` in your workspace:\n   ```json\n   {\n     \"mcpServers\": {\n       \"redmine\": {\n         \"command\": \"npx\",\n         \"args\": [\n           \"-y\",\n           \"mcp-client-http\",\n           \"http://127.0.0.1:8000/mcp\"\n         ],\n         \"disabled\": false\n       }\n     }\n   }\n   ```\n2. Save the file and restart Kiro. The Redmine tools will appear in the MCP panel.\n\n**Note:** Direct HTTP transport support in Kiro is limited. The above configuration uses `mcp-client-http` as a bridge to connect to HTTP MCP servers.\n\n</details>\n\n<details>\n<summary><strong>Generic MCP Clients</strong></summary>\n\nMost MCP clients use a standard configuration format. For HTTP servers:\n\n```json\n{\n  \"mcpServers\": {\n    \"redmine\": {\n      \"type\": \"http\",\n      \"url\": \"http://127.0.0.1:8000/mcp\"\n    }\n  }\n}\n```\n\nFor clients that require a command-based approach with HTTP bridge:\n\n```json\n{\n  \"mcpServers\": {\n    \"redmine\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-client-http\", \"http://127.0.0.1:8000/mcp\"]\n    }\n  }\n}\n```\n\n</details>\n\n### Testing Your Setup\n\n```bash\n# Test connection by checking health endpoint\ncurl http://localhost:8000/health\n```\n\n## Supported Redmine Plugins\n\nThe server works against a stock Redmine instance. Six optional plugins add\nmore, shown as seven rows below because CRM's deals carry their own flag. To\nuse one, install it on your Redmine server and set the matching env var.\nSkipping a plugin costs you only that plugin's features.\n\nPlugin tools appear in the client's tool list only when their env var is set; with the flag off they are not registered on the MCP surface at all.\n\n| Plugin | Vendor | Env var | What it adds |\n|---|---|---|---|\n| [Agile](https://www.redmineup.com/pages/plugins/agile) | RedmineUP | `REDMINE_AGILE_ENABLED` | `get_redmine_issue` returns `story_points`, `agile_sprint_id`, `agile_position`; `update_redmine_issue` accepts `story_points` |\n| [Checklists](https://www.redmineup.com/pages/plugins/checklists) | RedmineUP (Pro) | `REDMINE_CHECKLISTS_ENABLED` | 3 tools: `get_checklist`, `create_checklist_item`, `update_checklist_item` |\n| [Products](https://www.redmineup.com/pages/plugins/products) | RedmineUP | `REDMINE_PRODUCTS_ENABLED` | 1 tool: `manage_product`; with `REDMINE_DEALS_ENABLED` also `add_deal_product` |\n| [CRM](https://www.redmineup.com/pages/plugins/crm) | RedmineUP | `REDMINE_CRM_ENABLED` | 2 tools: `manage_contact`, `list_contact_tags`; plus the 2 shared CRM tools `manage_crm_note` and `list_crm_queries`, which either CRM flag enables (adds the `*_contacts` and note scopes to OAuth discovery when enabled) |\n| [CRM deals](https://www.redmineup.com/pages/plugins/crm) | RedmineUP (Pro) | `REDMINE_DEALS_ENABLED` | 3 tools: `manage_deal`, `list_deal_statuses`, `manage_deal_category`; plus the 2 shared CRM tools above, and `add_deal_product` when `REDMINE_PRODUCTS_ENABLED` is also set (adds the `*_deals` and note scopes to OAuth discovery when enabled). Same plugin as CRM, but the Light edition has no deals |\n| [DMSF](https://github.com/danmunn/redmine_dmsf) | danmunn (open source) | `REDMINE_DMSF_ENABLED` | 1 tool: `manage_document` |\n| [Additional Tags](https://github.com/alphanodes/additional_tags) | AlphaNodes (open source) | `REDMINE_TAGS_ENABLED` | `get_redmine_issue` returns a `tags` array; `create_redmine_issue` / `update_redmine_issue` accept `tag_list` |\n\nAgile and Additional Tags add fields to tools you already have, so they\nregister no new tools. The other five bring their own, which appear in\n`tools/list` either way but return a feature-disabled error until you set the\nflag. Tags also needs the `view_issue_tags`, `create_issue_tags`, and\n`edit_issue_tags` permissions on the Redmine server.\n\n## Available Tools\n\nA deployment can expose a subset of these with `REDMINE_MCP_ALLOW_TOOLS`; everything else disappears from `tools/list` and is refused by `call_tool`.\n\nThis MCP server provides 45 core tools for interacting with Redmine, plus 13 plugin tools that are listed only when the matching `REDMINE_*_ENABLED` flag is set (58 in total), and 1 operator tool exposed by `REDMINE_MCP_EXPOSE_ADMIN_TOOLS=true` (maximum of 59). A client connected to a vanilla Redmine sees just the 45 core tools. For full documentation of every tool, see the [Tool Reference](./docs/tool-reference.md).\n\n**Core tools (45, always available):** Project Management (9), Issue Operations (13), Time Tracking (4), Discovery / Enumeration (7), Search & Wiki (2), File Operations (4), Gantt (1), Interactive Apps (4), Meta (1).\n\n**Plugin-gated tools (13, listed only when their flag is set):** Checklists (3), Products (1), Contacts / CRM (2), Deals / CRM (3), shared CRM notes and saved queries (2, either CRM flag), deal product lines (1, deals plus products), Documents / DMSF (1). Each requires the matching Redmine plugin installed **and** its env flag set; with the flag off the tools are not registered on the MCP surface.\n\n**Operator tools (1, admin-gated):** `cleanup_attachment_files`, registered only when `REDMINE_MCP_EXPOSE_ADMIN_TOOLS=true`.\n\n<details>\n<summary><strong>Full tool list with descriptions</strong></summary>\n\n### Core tools (45, always available)\n\nThese tools require only a Redmine instance and credentials, with no extra plugins or feature flags.\n\n- **Project Management** (9 tools)\n  - [`list_redmine_projects`](docs/tool-reference.md#list_redmine_projects) - List accessible projects (active only unless `filters` asks for more), narrowed server-side and optionally paginated\n  - [`list_project_issue_custom_fields`](docs/tool-reference.md#list_project_issue_custom_fields) - List issue custom fields configured for a project\n  - [`list_redmine_versions`](docs/tool-reference.md#list_redmine_versions) - List versions/milestones for a project\n  - [`manage_redmine_version`](docs/tool-reference.md#manage_redmine_version) - Create, update, or delete a version/milestone\n  - [`list_project_members`](docs/tool-reference.md#list_project_members) - List members and roles of a project\n  - [`summarize_project_status`](docs/tool-reference.md#summarize_project_status) - Get comprehensive project status summary\n  - [`list_redmine_roles`](docs/tool-reference.md#list_redmine_roles) - List all roles defined in the Redmine instance (for discovering valid `role_ids`)\n  - [`get_project_modules`](docs/tool-reference.md#get_project_modules) - Retrieve the enabled modules for a project\n  - [`manage_project_member`](docs/tool-reference.md#manage_project_member) - Add, update, or remove a project membership\n\n- **Issue Operations** (13 tools)\n  - [`get_redmine_issue`](docs/tool-reference.md#get_redmine_issue) - Retrieve detailed issue information (supports journal pagination, watchers, relations, children)\n  - [`list_redmine_issues`](docs/tool-reference.md#list_redmine_issues) - List issues with flexible filtering (project, status, assignee, etc.)\n  - [`search_redmine_issues`](docs/tool-reference.md#search_redmine_issues) - Search issues by text query\n  - [`create_redmine_issue`](docs/tool-reference.md#create_redmine_issue) - Create new issues, with optional file attachments via the `uploads` parameter\n  - [`update_redmine_issue`](docs/tool-reference.md#update_redmine_issue) - Update existing issues, with optional file attachments via the `uploads` parameter (combine with `notes` to attach files to a journal note)\n  - [`delete_redmine_issue`](docs/tool-reference.md#delete_redmine_issue) - Hard-delete an issue with required confirmation flags and a cascade-impact preview before irreversible deletion.\n  - [`copy_issue`](docs/tool-reference.md#copy_issue) - Duplicate an existing issue with optional field overrides\n  - [`list_subtasks`](docs/tool-reference.md#list_subtasks) - List subtasks (child issues) of a given parent\n  - [`get_private_notes`](docs/tool-reference.md#get_private_notes) - Retrieve private notes on an issue\n  - [`manage_issue_relation`](docs/tool-reference.md#manage_issue_relation) - List, create, or delete issue relations\n  - [`manage_issue_watcher`](docs/tool-reference.md#manage_issue_watcher) - Add or remove a watcher on an issue\n  - [`manage_issue_note`](docs/tool-reference.md#manage_issue_note) - Edit a journal note's text or toggle its privacy\n  - [`manage_issue_category`](docs/tool-reference.md#manage_issue_category) - List, create, update, or delete issue categories\n  - Note: `get_redmine_issue` can include `custom_fields` and `update_redmine_issue` can update custom fields by name (for example `{\"size\": \"S\"}`).\n\n- **Time Tracking** (4 tools)\n  - [`list_time_entries`](docs/tool-reference.md#list_time_entries) - List time entries with filtering by project, issue, user, and date range\n  - [`manage_time_entry`](docs/tool-reference.md#manage_time_entry) - Create or update a time entry (use `user_id` to log on behalf of another user)\n  - [`list_time_entry_activities`](docs/tool-reference.md#list_time_entry_activities) - Discover available activity types for time entries\n  - [`import_time_entries`](docs/tool-reference.md#import_time_entries) - Bulk import time entries via sequential API calls with per-entry error reporting\n\n- **Discovery / Enumeration** (7 tools): help LLMs find valid IDs before calling create/update tools\n  - [`list_redmine_trackers`](docs/tool-reference.md#list_redmine_trackers) - List all trackers (Bug, Feature, Support, etc.)\n  - [`list_project_trackers`](docs/tool-reference.md#list_project_trackers) - List the trackers enabled for a specific project\n  - [`list_redmine_issue_statuses`](docs/tool-reference.md#list_redmine_issue_statuses) - List all issue statuses with their `is_closed` flag\n  - [`list_redmine_issue_priorities`](docs/tool-reference.md#list_redmine_issue_priorities) - List all priority levels\n  - [`list_redmine_users`](docs/tool-reference.md#list_redmine_users) - Filter/list users (admin-only; supports name and group filters)\n  - [`get_current_user`](docs/tool-reference.md#get_current_user) - Get the authenticated user's profile (works for non-admins)\n  - [`list_redmine_queries`](docs/tool-reference.md#list_redmine_queries) - List saved custom queries (read-only)\n\n- **Search & Wiki** (2 tools)\n  - [`search_entire_redmine`](docs/tool-reference.md#search_entire_redmine) - Global search across issues and wiki pages (Redmine 3.3.0+)\n  - [`manage_redmine_wiki_page`](docs/tool-reference.md#manage_redmine_wiki_page) - List, get, create, update, delete, or rename wiki pages\n\n- **File Operations** (4 tools)\n  - [`list_files`](docs/tool-reference.md#list_files) - List files uploaded to a project's Files section\n  - [`upload_file`](docs/tool-reference.md#upload_file) - Upload a new file to a project (from base64 content, a URL, or a server-side `file_path`), optionally tied to a version\n  - [`delete_file`](docs/tool-reference.md#delete_file) - Delete a file from a project\n  - [`get_redmine_attachment`](docs/tool-reference.md#get_redmine_attachment) - Download an attachment (works in both HTTP and stdio mode)\n\n- **Gantt** (1 tool)\n  - [`get_gantt_chart`](docs/tool-reference.md#get_gantt_chart) - Retrieve project timeline data: issues with dates, dependencies, and milestones\n\n- **Interactive Apps** (4 tools): render live UI in the chat via the [MCP Apps extension](https://github.com/modelcontextprotocol/ext-apps) (requires a client that supports it)\n  - [`show_triage_board`](docs/tool-reference.md#show_triage_board) - Render a project's issues as an interactive Kanban board grouped by status, with drag-to-change-status write-back\n  - [`get_triage_board_data`](docs/tool-reference.md#get_triage_board_data) - Board data source backing the board's Refresh action\n  - [`show_project_dashboard`](docs/tool-reference.md#show_project_dashboard) - Render a live project snapshot (open/closed, overdue, due this week, open-by-priority, recent activity) as an interactive dashboard, with click-through drill-ins to matching issue lists\n  - [`get_project_dashboard_data`](docs/tool-reference.md#get_project_dashboard_data) - App-only data source backing the dashboard's Refresh action\n\n- **Meta** (1 tool)\n  - [`get_mcp_server_info`](docs/tool-reference.md#get_mcp_server_info) - Report server version, auth mode, read-only state, the authenticated user (`current_user`), and which plugin-gated tool families are enabled. Use to detect deployment lag before relying on a recently-shipped fix, or to confirm who `assigned_to_id=\"me\"` resolves to.\n\n### Plugin-gated tools (13, opt in via env var)\n\nThese tools require a corresponding Redmine plugin installed on the server **and** the matching environment variable set to `true` on the MCP server. They are listed in `tools/list` only when their flag is set; with the flag off they are not registered on the MCP surface (and a direct call still returns a feature-disabled error).\n\n- **Checklists** (3 tools): set `REDMINE_CHECKLISTS_ENABLED=true`; requires the [RedmineUP Checklists Pro plugin](https://www.redmineup.com/pages/plugins/checklists)\n  - [`get_checklist`](docs/tool-reference.md#get_checklist) - Retrieve all checklist items for an issue\n  - [`create_checklist_item`](docs/tool-reference.md#create_checklist_item) - Add a new checklist item to an issue\n  - [`update_checklist_item`](docs/tool-reference.md#update_checklist_item) - Update a checklist item's text, done state, or position\n\n- **Products** (1 tool): set `REDMINE_PRODUCTS_ENABLED=true`; requires the [RedmineUP Products plugin](https://www.redmineup.com/pages/plugins/products)\n  - [`manage_product`](docs/tool-reference.md#manage_product) - List, get, create, or update products\n\n- **Contacts (CRM)** (2 tools): set `REDMINE_CRM_ENABLED=true`; requires the [RedmineUP CRM plugin](https://www.redmineup.com/pages/plugins/crm). In OAuth mode the flag also adds the CRM permissions to the advertised scopes, so grant them on the OAuth application and have users re-consent. Set `REDMINE_CRM_EDITION=pro` on a Pro install to allow the contact `list` filters the Light build does not register\n  - [`manage_contact`](docs/tool-reference.md#manage_contact) - List, get, create, update, delete, or assign/remove project association for contacts\n  - [`list_contact_tags`](docs/tool-reference.md#list_contact_tags) - Tags in use on contacts, with colors, for the `tags` filter and `tag_list`\n\n- **Deals (CRM)** (3 tools): set `REDMINE_DEALS_ENABLED=true`; requires the **Pro** edition of the same CRM plugin, and the `deals` project module enabled on the project. Deals have their own flag because the Light edition defines none of the deal permissions, so advertising them would break consent for Light deployments\n  - [`manage_deal`](docs/tool-reference.md#manage_deal) - List, get, create, update, or delete deals\n  - [`list_deal_statuses`](docs/tool-reference.md#list_deal_statuses) - Deal statuses (admin-only on the plugin side) and a project's deal categories, for use before creating a deal\n  - [`manage_deal_category`](docs/tool-reference.md#manage_deal_category) - List, create, rename, or delete a project's deal categories\n\n- **CRM notes and saved queries** (2 tools, shared): available when either `REDMINE_CRM_ENABLED` or `REDMINE_DEALS_ENABLED` is set; each call is gated on the flag matching the note's or query's source\n  - [`manage_crm_note`](docs/tool-reference.md#manage_crm_note) - Get, create, update, or delete CRM notes on contacts and deals\n  - [`list_crm_queries`](docs/tool-reference.md#list_crm_queries) - Saved contact or deal queries\n\n- **Deal product lines** (1 tool): needs both `REDMINE_DEALS_ENABLED=true` and `REDMINE_PRODUCTS_ENABLED=true`; the endpoint exists only when the Products plugin is installed next to CRM\n  - [`add_deal_product`](docs/tool-reference.md#add_deal_product) - Add a catalogue or free-form product line to a deal\n\n- **Documents (DMSF)** (1 tool): set `REDMINE_DMSF_ENABLED=true`; requires the [`redmine_dmsf` plugin](https://github.com/danmunn/redmine_dmsf)\n  - [`manage_document`](docs/tool-reference.md#manage_document) - List, get, create (upload), or update (new revision) DMSF documents\n\n### Operator tools (1, admin-gated)\n\nHidden from `tools/list` by default. Set `REDMINE_MCP_EXPOSE_ADMIN_TOOLS=true` to register them on the MCP surface. The underlying background tasks run regardless of this flag; exposing them only adds the option to drive them through MCP.\n\n- [`cleanup_attachment_files`](docs/tool-reference.md#cleanup_attachment_files) - Manually trigger cleanup of expired attachment files (the background cleanup task runs automatically regardless)\n\n</details>\n\n\n## Docker Deployment\n\n### Quick Start with Docker\n\n```bash\n# Configure environment\ncp .env.docker.example .env.docker\n# Edit .env.docker with your Redmine settings\n\n# Run with docker-compose\ndocker-compose up --build\n\n# Or run directly\ndocker build -t redmine-mcp-server .\ndocker run -p 8000:8000 --env-file .env.docker redmine-mcp-server\n```\n\n### Use the Published Image\n\nPrebuilt multi-architecture images (`linux/amd64`, `linux/arm64`) are published to\nthe GitHub Container Registry on each release, so you can run the server without\nbuilding it yourself:\n\n```bash\ndocker pull ghcr.io/jztan/redmine-mcp-server:latest\ndocker run -p 8000:8000 --env-file .env.docker ghcr.io/jztan/redmine-mcp-server:latest\n```\n\nPin to an exact version (e.g. `ghcr.io/jztan/redmine-mcp-server:2.2.0`) or track a\nminor series (e.g. `:2.2`). Published images are available starting from the next\nrelease.\n\n### Production Deployment\n\nUse the automated deployment script:\n\n```bash\nchmod +x deploy.sh\n./deploy.sh\n```\n\n## Troubleshooting\n\nIf you run into any issues, checkout our [troubleshooting guide](./docs/troubleshooting.md).\n\n## Roadmap\n\nSee the [roadmap](docs/roadmap.md) for planned features and future development.\n\n## Contributing\n\nContributions are welcome! Please see our [contributing guide](./docs/contributing.md) for details.\n\n## Contributors\n\nThank you to everyone who has helped improve this project through code, reviews, testing, and feature requests:\n\n<!-- contributors:start -->\n[@sebastianelsner](https://github.com/sebastianelsner) · [@mihajlovicjj](https://github.com/mihajlovicjj) · [@timcomport](https://github.com/timcomport) · [@aadnehovda](https://github.com/aadnehovda) · [@Vitexus](https://github.com/Vitexus) · [@Bricklou](https://github.com/Bricklou) · [@martindglaser](https://github.com/martindglaser) · [@LaurensRietveld](https://github.com/LaurensRietveld) · [@pdostal](https://github.com/pdostal) · [@stevehollis-orderflow](https://github.com/stevehollis-orderflow) · [@knasiotis](https://github.com/knasiotis) · [@azelcs](https://github.com/azelcs) · [@fionnb](https://github.com/fionnb) · [@goizper](https://github.com/goizper) · [@mmahmed](https://github.com/mmahmed) · [@andilem](https://github.com/andilem) · [@gino8080](https://github.com/gino8080)\n<!-- contributors:end -->\n\n<a href=\"https://github.com/jztan/redmine-mcp-server/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=jztan/redmine-mcp-server\" alt=\"Contributors\" />\n</a>\n\nPer-release contributor credits are listed in the [Changelog](./CHANGELOG.md).\n\nThanks also to [RedmineUP](https://www.redmineup.com) for providing an evaluation copy of CRM PRO, which let the CRM tools be verified against a real Pro instance.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Blog posts\n\n**The story behind the releases.** Building this server keeps surprising me: full API access that turned out to be a mistake, 69 tools that had to become 43, an OAuth scope bug that only surfaced when a contributor ran the flow against a real Redmine 6 instance. Plenty of the sharpest lessons arrived from other people's deployments rather than mine. I write about that thinking in [The Dispatch](https://blog.jztan.com/newsletter/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server). Come along if that's your kind of thing.\n\nBackground, design notes, and postmortems from building this server:\n\n**Getting started**\n\n- [Redmine MCP Server: Give AI Agents Live Project Data](https://blog.jztan.com/redmine-mcp-server-for-ai-agents/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server): What the server does, why the tool surface is curated rather than a full API mirror, and how to choose a Redmine MCP server\n- [How I linked a legacy system to a modern AI agent with MCP](https://blog.jztan.com/how-i-linked-a-legacy-system-to-a-modern-ai-agent/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server): The problem that started this project, and the first two read-only tools\n\n**Tool design & architecture**\n\n- [Designing Reliable MCP Servers: 3 Hard Lessons in Agentic Architecture](https://blog.jztan.com/i-gave-my-ai-agent-full-api-access-it-was-a-mistak/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server): Why full API access to an agent was a mistake, and what replaced it\n- [MCP Tool Sprawl: How I Cut 69 Tools to 43 With a Decorator](https://blog.jztan.com/mcp-tool-sprawl-consolidation/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server): The v2 consolidation that cut context overhead and sharpened agent tool selection\n\n**Production**\n\n- [How to Evaluate an MCP Server With an LLM: 17 Bugs Found and Fixed](https://blog.jztan.com/evaluate-mcp-server-with-llm/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server): Driving the server with an agent surfaced 17 problems the test suite missed, including a `role_ids=[True]` bug that quietly assigned an elevated role\n- [What It Actually Takes to Ship a Production MCP Server for Redmine](https://blog.jztan.com/what-it-actually-takes-to-ship-a-production-mcp-server-for-redmine/?utm_source=github&utm_medium=referral&utm_campaign=redmine-mcp-server): The full journey from prototype to production\n",
  "bytes": 49401,
  "sha": "dcf87b7257bd609750a43ed69f64be429690e1c4455444cf7709f0aec1d4a7fa",
  "repo_slug": "jztan/redmine-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_jztan_redmine_mcp_server_733f7aa9/readme"
}