Codumentor logo Codumentor

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):

ToolApprovalPurpose
redmine_search_issuesnone (read)List/filter issues (project_id, status, assigned_to, query, limit).
redmine_get_issuenone (read)Fetch one issue with journals/attachments.
redmine_list_projectsnone (read)List visible projects.
redmine_list_time_entriesnone (read)Sum/list logged hours by user, project, issue, and date range.
redmine_api_requestredmine.api_request (writes only)Generic escape hatch for any endpoint without a dedicated tool.
redmine_create_issueredmine.create_issueCreate an issue (gated).
redmine_update_issueredmine.update_issueUpdate issue fields (gated).
redmine_add_noteredmine.add_noteAdd 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:

  1. In Redmine: My account → API access key → Show.
  2. In Codumentor: Settings → User Secrets → Add, name it redmine_api_key (or whatever api_key_secret is 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