Redmine Plugin
The redmine plugin gives the agent a set of Redmine issue tools — search, read, list time entries, create, update, comment, plus a generic API escape hatch — that authenticate as the calling user. Each tool resolves the user's personal Redmine API key from the user_secrets registry at call time, so there is no shared instance key and no MCP server to run.
Why a native plugin (not MCP)
A Redmine MCP server would be a separate process to host (HTTP MCP) or a shared subprocess that cannot carry per-user identity (stdio MCP — its environment is fixed at spawn and shared across all sessions). A native plugin sidesteps both: the per-turn SecretsRegistry already holds the authenticated user's secrets, so redmine_* tools read the calling user's key directly via ${secret:…} resolution. See doc/core-dev/design/user-secrets.md for the secret-resolution model.
Overview
Eight tools, registered via onPromptAssemble (gated by target_agents):
| Tool | Approval | Purpose |
|---|---|---|
redmine_search_issues | none (read) | List/filter issues (project_id, status, assigned_to, query, limit). |
redmine_get_issue | none (read) | Fetch one issue with journals/attachments. |
redmine_list_projects | none (read) | List visible projects. |
redmine_list_time_entries | none (read) | Sum/list logged hours by user, project, issue, and date range. |
redmine_api_request | redmine.api_request (writes only) | Generic escape hatch for any endpoint without a dedicated tool. |
redmine_create_issue | redmine.create_issue | Create an issue (gated). |
redmine_update_issue | redmine.update_issue | Update issue fields (gated). |
redmine_add_note | redmine.add_note | Add a note/comment to an issue (gated). |
Write tools declare a PermissionRequest, so they flow through the runtime's PermissionProvider exactly like any other side-effectful tool.
Logged-hours questions
Issue-level spent_hours are lifetime totals and cannot be filtered by date, so they can't answer "how many hours did I log this month". redmine_list_time_entries hits Redmine's /time_entries.json (defaulting user_id to me), accepts a from/to spent_on range, and returns total_hours alongside the individual entries.
Generic API escape hatch
redmine_api_request covers endpoints with no dedicated tool (e.g. enumerations, custom queries). It is the scoped alternative to shell + curl: it reuses the same per-user key injection, so the model never assembles auth itself, and it stays auditable. GET runs unattended; POST/PUT/DELETE are gated through redmine.api_request. Prefer a dedicated tool when one exists.
Configuration
plugins:
- module: codumentor.plugins.redmine
class: RedminePlugin
args:
enabled: true
url: https://redmine.example.com # required
api_key_secret: redmine_api_key # per-user secret name (default)
default_project: "" # optional fallback project id
target_agents: all # main | subagent | all
dependencies: [user_secrets] — the plugin only authenticates via per-user secrets, so the user_secrets plugin must be loaded.
Per-user setup
Each user adds their personal Redmine API key as a secret:
- In Redmine: My account → API access key → Show.
- In Codumentor: Settings → User Secrets → Add, name it
redmine_api_key(or whateverapi_key_secretis set to), paste the key.
When a tool runs without the secret set, it returns a clear, non-retryable message telling the user to add it — no network call is made, and nothing leaks into the transcript.
To make step 2 obvious, the plugin declares this secret to the User Secrets pane via ui_manifest() secret_requirements (using the configured api_key_secret name). The pane then shows "Redmine API key — needed by Redmine" with set/unset state and a one-click prefill, so the user never has to guess the name. See Secret requirements.
Notes
- Redmine validation errors (HTTP 422 — e.g. a required custom field) are surfaced verbatim to the model so it can correct the call.
redmine_update_issueonly forwards a known set of fields (subject,description,status_id,tracker_id,priority_id,assigned_to_id,done_ratio,custom_fields); unknown keys are dropped.- The instance-keyed
bug_reportRedmine backend (bug_report/backends/redmine.py) is a separate, submission-only path and is unaffected by this plugin.