- Endpoints
- Enable the server
- Create an OAuth application for each client
- Connect a client
- Prompt for your AI agent
- Scopes and permissions
- Access with an API key
- Try it with curl
- Security recommendations
The Model Context Protocol (MCP) is an open standard that lets AI clients call tools of external systems. The Agent plugin makes your Redmine an MCP server: a client such as Claude, ChatGPT, Cursor, Codex or Gemini CLI can search and read issues, projects, wiki and time entries, create and update issues, add notes and log time, as the signed-in user and within that user's permissions. The full list is in MCP tools reference.
Endpoints
| Method and path | Purpose |
|---|---|
POST /mcp |
the MCP endpoint (Streamable HTTP, JSON-RPC 2.0). GET /mcp answers 405 |
GET /.well-known/oauth-protected-resource/mcp |
OAuth resource metadata (RFC 9728) |
GET /.well-known/oauth-authorization-server |
OAuth authorization server metadata (RFC 8414) |
GET /mcp/authorize |
authorization endpoint advertised to MCP clients, narrows the scopes and redirects to /oauth/authorize |
POST /oauth/token, POST /oauth/revoke |
token and revocation endpoints of Redmine (Doorkeeper) |
The server answers 404 on all of them until the MCP server is enabled. Protocol: version 2025-03-26, methods initialize, ping, tools/list, tools/call; notifications get 202. The server exposes tools only (no resources or prompts).
Enable the server
- Enable the REST web service: Administration → Settings → API → Enable REST web service. Core Redmine accepts OAuth tokens and API keys only when it is on.
- Open Administration → Plugins → Redmine Agent → Configure and tick Enable MCP server. The MCP server URL is shown below the field:
https://your-redmine.example.com/mcp. - Make sure Administration → Settings → General → Host name and path and the protocol are correct: OAuth metadata is built from them (important behind a reverse proxy).
Create an OAuth application for each client
- Open Administration → Applications → New application.
- Give it a name, enter the Redirect URI of the client (table below) and tick the scopes that the client may use. Tools of the client are limited to the ticked scopes and the roles of the user. The client may request more scopes: it gets only the ticked ones.
- Save. Redmine shows the secret only once; copy it with the UID (client ID). Every application is confidential, so each client needs both.
Connect a client
| Client | Redirect URI | Where to enter the client ID and secret |
|---|---|---|
| claude.ai, Claude Desktop | https://claude.ai/api/mcp/auth_callback |
Settings → Connectors → Add custom connector → Advanced settings → OAuth Client ID and Secret |
| Claude Code | http://localhost:8765/callback |
see the command below |
| ChatGPT | the callback URL shown in the connector form | Connector → Advanced → User-defined OAuth client |
| Codex, Gemini CLI, Cursor and other local clients | the loopback URL from the OAuth settings of the client | the OAuth client ID and secret settings of the client |
MCP_CLIENT_SECRET=CLIENT_SECRET claude mcp add --transport http \ --client-id CLIENT_ID --client-secret --callback-port 8765 \ redmine https://your-redmine.example.com/mcp
Redmine ignores the port of a redirect URI only for 127.0.0.1, so a localhost redirect needs a fixed callback port. After adding the server the client opens the Redmine consent page: sign in, review the scopes and approve.
Prompt for your AI agent
Paste this prompt into the AI agent that should be connected (Claude Code, Codex, Gemini CLI, Cursor…), after replacing the three values in capitals. The agent adds the server with the commands of its own client and checks the connection. Do not paste a secret into a shared chat: use a local agent.
Connect the Redmine MCP server to this AI client and check that it works. Server URL: https://YOUR-REDMINE/mcp (Streamable HTTP, JSON-RPC 2.0, name "redmine") Authentication: OAuth 2.0 with a pre-registered client client ID: CLIENT_ID client secret: CLIENT_SECRET redirect URI: http://localhost:8765/callback (fixed callback port 8765) Steps: 1. Find out which client you run in (Claude Code, Codex, Gemini CLI, Cursor, other) and add the server with the MCP command or the config file of that client, HTTP transport, the name "redmine", the client ID and the secret. For Claude Code the command is: MCP_CLIENT_SECRET=CLIENT_SECRET claude mcp add --transport http --client-id CLIENT_ID --client-secret --callback-port 8765 redmine https://YOUR-REDMINE/mcp 2. Start the OAuth sign-in and tell me to approve the access in the browser. Do not invent another redirect URI or port. 3. When the sign-in is done, call get_current_user and list_projects and show me who I am and the first projects. 4. Do not print the secret again, do not write it to a file inside a repository. If the client can only send a fixed bearer token, use the API key instead of OAuth: send the header "Authorization: Bearer API_KEY" and skip steps 1-2 of the sign-in.
After the connection works you can give the agent these working rules (put them into its memory file, for example CLAUDE.md or AGENTS.md):
Redmine is available through the MCP server "redmine". Rules: - Before creating an issue call get_project_metadata for the project to get valid tracker, status, priority, category and custom field ids. - Find things with list_issues (filters: project, status, assigned_to "me", version_id, query_id) or search_redmine; do not guess ids. - For long lists pass fields (only the columns you need) or compact true, and page with limit and offset. - Call get_issue with include only for what you need (journals, relations, children, attachments). - Writes happen immediately as my Redmine user: before delete_*, manage_* or send_helpdesk_reply tell me what you are about to do and wait for my confirmation. - Never put secrets or personal data into issues, notes or wiki pages.
Scopes and permissions
A tool is listed and callable when both the token scopes and the roles of the user grant the Redmine permission that the tool needs (for example view_issues, add_issues, log_time), in any project the user can see; each call is checked again against the project. The scopes advertised by the server are the permissions of the registered tools, view_private_notes (private notes in get_issue) and the scopes that plugins declare for their extensions (tags, checklists). Private notes and issues of projects the user cannot access are never returned.
Access with an API key
Clients that can only send a fixed bearer token (xAI, Grok and others) can use the user's API key. Tick Allow MCP access with API key in the plugin settings (off by default) and send:
Authorization: Bearer API_KEY
or the header X-Redmine-API-Key. The key query parameter and X-Redmine-Switch-User are not supported. An API key has no scopes and no expiry, so the client gets every right of the user, writes included. Prefer OAuth. Without this setting an API key is rejected with 401.
Try it with curl
Obtain an access token through the OAuth flow of your client (or use an API key if allowed) and call:
curl -s https://your-redmine.example.com/mcp \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Call a tool:
curl -s https://your-redmine.example.com/mcp \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"list_issues","arguments":{"project":"support","assigned_to":"me","limit":5}}}'
Security recommendations
- Writes run immediately, without a confirmation, as the user and in every project the user can reach. An external AI that reads a hostile issue or wiki page could be tricked into writing on the user's behalf. Keep the tool approval prompt of the client enabled and tick only the scopes the client needs (for example read-only scopes for reporting clients).
- Create one application per client so that it can be revoked separately (delete the application in Administration → Applications).
- Tokens are kept out of the logs. A tool failure returns
isError: truewith "Tool failed"; details stay in the Rails log. - Use HTTPS.
See also MCP tools reference and MCP troubleshooting.