v2.3.0: OAuth-proxy auth mode and reliable test runs
Highlights
This release adds a new opt-in authentication mode, REDMINE_AUTH_MODE=oauth-proxy. In this mode the MCP server acts as the OAuth authorization server that clients talk to, serving Dynamic Client Registration plus /authorize, /token, and /register, while proxying the upstream flow to Redmine/Doorkeeper and keeping user consent on Redmine. This unlocks clients that require DCR or CIMD without Redmine having to serve RFC 8414 metadata or implement DCR, which the existing introspection-based oauth mode could not provide on split-host deployments.
Security is sensible by default: oauth-proxy restricts client redirect URIs to loopback (http://localhost:*, http://127.0.0.1:*), so a registered client cannot point the flow at a remote target unless you explicitly allow it. Secrets can be supplied via *_FILE env vars for Docker and Kubernetes.
Legacy and oauth modes are unchanged, and legacy remains the default. This release also fixes --all test runs that were silently skipping every integration test.
Changes
Added
- New opt-in
REDMINE_AUTH_MODE=oauth-proxymode: the server serves DCR,/authorize,/token, and/register, proxies the upstream flow to Redmine/Doorkeeper, and keeps consent on Redmine. Supports DCR/CIMD clients on split-host deployments without Redmine serving RFC 8414 metadata or DCR. Requires a stableREDMINE_MCP_JWT_SIGNING_KEY. (#153) /healthnow probes Redmine introspection in bothoauthandoauth-proxymodes.- Mount the app behind a public base path via
REDMINE_MCP_BASE_URL. - Supply secrets via
*_FILEenv vars for Docker/Kubernetes. oauth-proxymode supports hosted clients viaREDMINE_MCP_ALLOWED_CLIENT_REDIRECT_URIS(comma- or space-separated glob patterns, or*to accept any redirect URI).
Fixed
python tests/run_tests.py --allnow actually runs the integration suite. It runs the unit phase and the integration phase as two separate pytest processes so each gets the correct environment, and integration coverage is appended. (#156)
Changed
- Secret
*_FILEenv vars now raise a clear error naming the variable and path when the referenced file cannot be read, instead of a bareFileNotFoundError.
Security
oauth-proxymode restricts client redirect URIs to loopback (http://localhost:*,http://127.0.0.1:*) by default, preventing a registered client from pointing the flow at a remote target out of the box.
Acknowledgements
Thanks to @aadnehovda for contributing:
- designed and implemented the
oauth-proxyauthentication mode backed by FastMCPOAuthProxy, and refactored the remote OAuth setup intoRedmineAuthProvider(#153)
Thanks to @timcomport for contributing:
- verified the
oauth-proxymode end to end on a split-host VS Code + Redmine 6.1.1 deployment and caught the Step 4 authorization-server metadata path discrepancy (#140)
Installation
pip install redmine-mcp-server==2.3.0