Skip to content

feat(mcp): apply rate_limit_rpm live and expose it in settings#70

Merged
jaymeklein merged 1 commit into
mainfrom
feat/mcp-rate-limit-live
Jul 20, 2026
Merged

feat(mcp): apply rate_limit_rpm live and expose it in settings#70
jaymeklein merged 1 commit into
mainfrom
feat/mcp-rate-limit-live

Conversation

@jaymeklein

@jaymeklein jaymeklein commented Jul 17, 2026

Copy link
Copy Markdown
Owner

What

The MCP per-key rate limit (mcp.rate_limit_rpm) can now be changed from Settings → Config → MCP server and takes effect on the next MCP request — no restart.

Why

The limiter captured its rpm when the /mcp app was mounted at startup, so a config PUT persisted to config.yaml and updated live config but left the running limiter on the boot-time value until an API restart. apply_config swaps the config singleton, never the mount.

How

  • TokenBucketRateLimiter takes rpm as a Callable[[], int], resolved on every allow() from live config via get_mcp_config() (mirrors the existing get_search_config()). A lowered limit re-clamps an already-full bucket on the next call; a raised one widens it immediately.
  • New MCP server section on the config page edits the limit; it round-trips enabled / max_results untouched.
  • MCPConfig.rate_limit_rpm is bounded ge=1: 0 denies every request and a negative rate accrues token debt that outlives the fix (the bucket is never rebuilt), so a bad value is now a clean 422 instead of a live lockout.

Tests / gate

New regression tests: the limit change applies without rebuilding the limiter; a lowered limit bites immediately; config rejects rpm < 1. ruff + mypy + unit/integration green; docs/openapi.yaml regenerated (adds minimum: 1).

The MCP rate limiter captured rate_limit_rpm when the /mcp app was mounted at
startup, so a config change only took effect after an API restart. Resolve the
limit per request instead (a Callable read from the live config), so a save
binds on the next MCP call — the middleware is built once and never rebuilt,
but now reads the value each time. A lowered limit re-clamps an already-full
bucket on the next call; a raised one widens it immediately.

Add a matching MCP section to the settings config page to edit the limit, and
bound the field with ge=1 (0 would deny every request; a negative rate accrues
token debt that outlives the correction, since the bucket is never rebuilt) so
a bad value is a clean 422 rather than a live lockout.

Regenerate docs/openapi.yaml and update the README / MCP guide accordingly.
@jaymeklein
jaymeklein merged commit e69bc32 into main Jul 20, 2026
8 checks passed
jaymeklein pushed a commit that referenced this pull request Jul 21, 2026
Integrate the live MCP rate-limit (#70), sticky pagination (#71), and
indexing retry-failed (#72) work that landed on main.

Resolved two conflicts in the MCP layer: build_mcp_middleware keeps #70's
live rate_limit_rpm callable alongside this branch's per-user
resolve_principal, dropping the now-obsolete master_key param (auth is
DB-backed via authenticate_api_key, which already handles the master key).
The middleware tests construct TokenBucketRateLimiter with a callable to
match #70's new signature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant