Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
418becc
test(oauth): guard scope-overlap assertion against inactive token
jztan Jun 27, 2026
6d7316a
fix(deps): bump joserfc to clear CVE-2026-48990
jztan Jun 27, 2026
d7e0331
chore: bump version to v2.4.0
jztan Jun 27, 2026
9181b6f
docs(roadmap): update for v2.4.0 and park MCP Prompts item
jztan Jun 27, 2026
51a3f21
docs(roadmap): tidy release sections and add dated spec gate status
jztan Jun 27, 2026
98c2979
docs(roadmap): promote MCP Apps to a committed near-term track
jztan Jun 27, 2026
2b26e10
feat: add _per_user key resolution for legacy-per-user auth
jztan Jun 27, 2026
509bd28
feat: fail closed at startup unless legacy-per-user is attested
jztan Jun 27, 2026
f18848e
feat: build per-request client from X-Redmine-API-Key in legacy-per-u…
jztan Jun 27, 2026
99ced18
feat: surface PerUserAuthError as a clean tool error envelope
jztan Jun 27, 2026
6551f3d
feat: add reachability-only health probe for legacy-per-user mode
jztan Jun 27, 2026
1c59015
feat: opt-in per-user identity audit logging
jztan Jun 27, 2026
92575f2
docs: document legacy-per-user auth mode and its threat model
jztan Jun 27, 2026
963f3c5
docs: document legacy-per-user mode in docker env sample and tidy prose
jztan Jun 27, 2026
c7a406a
fix: harden per-user key validation and close test gaps
jztan Jun 27, 2026
ba5c3fc
Merge branch 'feature/legacy-per-user-auth' into develop
jztan Jun 27, 2026
1d1b1ba
docs(roadmap): note legacy-per-user auth mode queued on develop
jztan Jun 27, 2026
8578827
docs: streamline README authentication section
jztan Jun 28, 2026
a268e5f
docs: add legacy-per-user architecture diagram
jztan Jun 28, 2026
825a440
feat: harvest tracker and checklist tools from community forks
jztan Jun 28, 2026
1b40b68
chore(deps): bump joserfc from 1.7.1 to 1.7.2
dependabot[bot] Jun 29, 2026
d8777d5
feat: add upload file_path allowlist roots env reader
jztan Jun 30, 2026
ae2aae9
feat: add allowlist-gated local file resolution for uploads
jztan Jun 30, 2026
aa2c7ff
feat: unify upload content resolution and add file_path to upload_file
jztan Jun 30, 2026
c2bcb2f
fix: use canonical exactly-one-source upload error message
jztan Jun 30, 2026
d6fc392
feat: add issue upload descriptor builder with per-call cap
jztan Jun 30, 2026
7f957be
feat: support file uploads on update_redmine_issue
jztan Jun 30, 2026
99479f1
fix: augment upload return on update_redmine_issue autofill retry path
jztan Jun 30, 2026
37095a2
feat: support file uploads on create_redmine_issue
jztan Jun 30, 2026
16091db
docs: document issue file uploads and file_path source
jztan Jun 30, 2026
dd5e245
docs: correct upload file roots delimiter and journal_id wording
jztan Jun 30, 2026
7111a8b
docs: remove em dashes from upload file_path documentation
jztan Jun 30, 2026
8b55433
docs: note nullable journal_id and add create read-only upload test
jztan Jun 30, 2026
d919e44
Merge branch 'feature/issue-file-uploads' into develop
jztan Jun 30, 2026
00a763f
Merge pull request #170 from jztan/dependabot/uv/joserfc-1.7.2
jztan Jul 1, 2026
2695b9b
chore: bump version to v2.5.0
jztan Jul 3, 2026
4f107d7
docs: refresh roadmap for v2.5.0 release
jztan Jul 3, 2026
314376b
docs: reframe MCP Apps first slice to proceed independent of #168 poll
jztan Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ ATTACHMENT_EXPIRES_MINUTES=60
# REDMINE_AUTOFILL_REQUIRED_CUSTOM_FIELDS=false
# REDMINE_REQUIRED_CUSTOM_FIELD_DEFAULTS={}

# --- legacy-per-user auth (advanced; Redmine too old for OAuth) ---
# Each user's MCP client sends its own Redmine API key in an
# X-Redmine-API-Key header. The server cannot verify TLS itself, so you MUST
# attest that it sits behind a TLS-terminating proxy and that the proxy does
# not forward client X-Forwarded-Proto. Firewall the app port. Prefer
# dedicated limited-permission Redmine accounts. Revoke a user by
# regenerating their API key in Redmine.
# REDMINE_AUTH_MODE=legacy-per-user
# REDMINE_PER_USER_TRUST_PROXY=true
# REDMINE_PER_USER_AUDIT_IDENTITY=false

# SSL options (optional)
# REDMINE_SSL_VERIFY=true
# REDMINE_SSL_CERT=/path/to/ca-bundle.crt
Expand Down
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ CLEANUP_INTERVAL_MINUTES=10
# Default expiry time for downloaded attachments (in minutes)
ATTACHMENT_EXPIRES_MINUTES=60

# OS path separator (os.pathsep) separated extra directories allowed as `file_path` upload
# sources for create/update issue and upload_file. ATTACHMENTS_DIR is always
# allowed. Leave unset to restrict uploads to ATTACHMENTS_DIR only.
REDMINE_MCP_UPLOAD_FILE_ROOTS=

# Read-only mode (optional)
# When enabled, write operations (create/update/delete) are blocked
# REDMINE_MCP_READ_ONLY=false
Expand Down Expand Up @@ -145,6 +150,17 @@ ATTACHMENT_EXPIRES_MINUTES=60
# REDMINE_AUTOFILL_REQUIRED_CUSTOM_FIELDS=false
# REDMINE_REQUIRED_CUSTOM_FIELD_DEFAULTS={}

# --- legacy-per-user auth (advanced; Redmine too old for OAuth) ---
# Each user's MCP client sends its own Redmine API key in an
# X-Redmine-API-Key header. The server cannot verify TLS itself, so you MUST
# attest that it sits behind a TLS-terminating proxy and that the proxy does
# not forward client X-Forwarded-Proto. Firewall the app port. Prefer
# dedicated limited-permission Redmine accounts. Revoke a user by
# regenerating their API key in Redmine.
# REDMINE_AUTH_MODE=legacy-per-user
# REDMINE_PER_USER_TRUST_PROXY=true
# REDMINE_PER_USER_AUDIT_IDENTITY=false

# SSL Certificate Configuration (Optional)
# Enable/disable SSL certificate verification (default: true)
# WARNING: Only set to false for development/testing environments!
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,38 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [2.5.0] - 2026-07-04
### Added
- `create_redmine_issue` and `update_redmine_issue` now accept an `uploads`
parameter to attach files to an issue (and to a journal note when combined
with `notes`), resolving each file from `content_base64`, `source_url`, or a
new on-disk `file_path` source.
- `upload_file` gains a `file_path` source for files already on the server.
- New `REDMINE_MCP_UPLOAD_FILE_ROOTS` setting to allowlist `file_path` upload
directories (defaults to `ATTACHMENTS_DIR`).
- `legacy-per-user` auth mode: per-request Redmine API key via an
`X-Redmine-API-Key` header, for Redmine instances too old for OAuth. Opt-in
and fail-closed (`REDMINE_PER_USER_TRUST_PROXY` required); keys are redacted
from logs; optional identity audit via `REDMINE_PER_USER_AUDIT_IDENTITY`.
- `tracker` field in issue serialization and the `fields` selector for issue listing/search tools.
- `list_project_trackers` tool for project-scoped tracker discovery.
- `create_checklist_item` tool (RedmineUP Checklists) and `is_section` in checklist output.

## [2.4.0] - 2026-06-27
### Added
- Promotional demo page under `pages/`, deployed to GitHub Pages on version tags via a new `deploy-demo.yml` workflow. It is a scripted, client-side walkthrough of an AI agent triaging a sample Redmine sprint backlog (list, read, reassign, comment, log time, close), with tool-call request/response JSON that matches the server's real response shapes, a Kanban board that updates as the agent works, and a light/dark theme toggle. No live Redmine is connected.

### Fixed
- `get_redmine_issue` now returns journal field-change `details` (status, assignee, custom-field edits) and no longer drops journals that have no note text. The `_journals_to_list` helper previously skipped any journal whose `notes` was empty via `if not notes: continue` and never serialized the `details` array, so field-only history was lost and `details` was missing even on journals with notes. Journals are now kept when they have a note **or** field-change details, and each entry includes `details` (`property`, `name`, `old_value`, `new_value`) plus `private_notes`. `get_private_notes` exposes `details` as well. ([#161](https://github.com/jztan/redmine-mcp-server/issues/161))

### Security
- Add a direct `joserfc>=1.6.7,<2` floor to clear CVE-2026-48990 (CWE-400 uncontrolled resource consumption). `joserfc` 1.3.4 through 1.6.5 fails to apply `JWSRegistry.max_payload_length` to RFC 7797 unencoded (`b64=false`) JWS payloads, allowing oversized payloads to be deserialized; the fix landed in 1.6.6. `joserfc` is a transitive dependency via `authlib` and `fastmcp`, so the direct floor ensures the fix reaches PyPI installs, not only the pinned lockfile. The lock now resolves `joserfc` 1.7.1.
- Free-form journal field-change values now receive the same prompt-injection wrapping as journal notes. Custom-field values (`cf`), `description`/`subject` edits, and attachment filenames in `details` are wrapped in `<insecure-content-{boundary}>` tags, so the newly surfaced field-change history cannot smuggle injected instructions past an LLM consumer. Structured values (status, assignee, priority IDs, dates, numbers) are left raw to avoid bloating output with boundary tags. ([#161](https://github.com/jztan/redmine-mcp-server/issues/161))

### Tests
- `test_scope_advertising_subset_of_sandbox_scopes` now asserts the introspected test token is active before checking scope overlap. A stale `REDMINE_OAUTH_TEST_TOKEN` introspects with an empty scope, which previously made the test fail with a misleading "name drift between oauth_scopes.py and live Doorkeeper config" message; the active-token guard surfaces the real cause (re-mint the bearer) instead.

### Contributors
- @martindglaser — fix missing journal field-change details in `get_redmine_issue` ([#163](https://github.com/jztan/redmine-mcp-server/pull/163))

Expand Down Expand Up @@ -919,6 +942,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Comprehensive authentication support (username/password and API key)
- Docker containerization support

[2.5.0]: https://github.com/jztan/redmine-mcp-server/releases/tag/v2.5.0
[2.4.0]: https://github.com/jztan/redmine-mcp-server/releases/tag/v2.4.0
[2.3.1]: https://github.com/jztan/redmine-mcp-server/releases/tag/v2.3.1
[2.3.0]: https://github.com/jztan/redmine-mcp-server/releases/tag/v2.3.0
[2.2.0]: https://github.com/jztan/redmine-mcp-server/releases/tag/v2.2.0
Expand Down
Loading