🎉 Release v0.22.50
📦 Docker Images
# Docker Hub
docker pull agigante80/actual-mcp-server:0.22.50
# GitHub Container Registry
docker pull ghcr.io/agigante80/actual-mcp-server:0.22.50📝 Changelog
- chore(release): bump version to 0.22.50 (ba26e6d)
- feat(auth): one static Bearer token per client with its own budgets (#572) (555eb4e)
📊 What this release ships
@actual-app/apipinned at26.10.0- All 84 MCP tools registered and validated on this tag
🔒 Security
This release has been scanned for vulnerabilities. Check the Security tab for details.
Full Changelog: v0.22.49...v0.22.50
Notes: one static token per client (#572)
- New opt-in
MCP_STATIC_TOKENS: several static Bearer tokens, each bound to its own principal and budget allow-list, for multi-client setups without OIDC (for example one LobeChat assistant per budget). Format:{"alice":{"tokenEnv":"MCP_TOKEN_ALICE","budgets":["Home"]}}; the token value lives in the namedMCP_TOKEN_*variable and every such value is redacted from logs. Tokens must be at least 32 characters, unique and RFC 6750 safe; startup refuses otherwise, naming variables, never values.MCP_SSE_AUTHORIZATIONkeeps working unchanged as the unrestrictedstatic-bearerprincipal. Setup:docs/guides/AI_CLIENT_SETUP.md; security model:docs/SECURITY_AND_PRIVACY.mdsection 1b. - Enforcement reuses the OIDC chokepoints: listings, switch (a denied budget reads as not found), import and the per-operation ACL. Tokens are compared as SHA-256 digests with
timingSafeEqualagainst every entry. - Behaviour change for OIDC: a restricted principal whose allow-list excludes the default budget now starts on its first permitted budget (registry order) instead of getting
Budget ACL:on every call until it switches. - Still one open budget per server process: alternating clients on different budgets reload under the global lock (measured 2 s to 17 s on the test budgets, see #572). For heavy concurrent use of different budgets, run one server per budget.
- Fixed in passing: a startup failure printed its message one character per line.
- Proof: unit rows with 9 of 9 mutation checks killed, a live two-token run recorded on #572, and the full dual-transport run passed with zero residue on ba26e6d.
- Thanks to @jospaquim, who proposed and first implemented this in PR #569 (co-author on the commit).