[FEATURE]: Scope per-user rate limits by MCP server in cpex-rate-limiter

Open
#172 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
python, redis, rust, yaml

Research direction

Start in plugins/rust/python-package/cpex-rate-limiter/ by reading RateLimiterEngine, EngineConfig, RateLimiterPluginCore, and the .pyi stub, then trace check() and check_async() for memory and Redis paths. Review plugin-manifest.yaml and the existing rate-limit tests. Done means server-scoped UUID keys, global-scope compatibility, documented migration impact, and coverage for both acceptance scenarios.

Written by the indexing model from the issue text.

Description

client-mrk enhancement SHOULD

Summary

Upstream requirement tracked in IBM/mcp-context-forge#6230.

ContextForge gateway operators need the RateLimiterPlugin (cpex-rate-limiter) to track per-user quotas independently per MCP server, so that exhausting a user's budget on MCP-A does not block that user from reaching MCP-B.

Current behaviour

The Rust engine's check() / check_async() accept user, tenant, and tool as dimensions. The resulting Redis key shape is effectively:

{prefix}:user:{user_id}:{window}

Exhausting this bucket blocks the user across all MCP servers simultaneously.

Required behaviour

Add a stable server_id dimension so the bucket key becomes:

{prefix}:user:{user_id}:server:{server_id}:{window}

A user-facing user_scope config knob (e.g. user_scope: "server") should opt in to this behaviour, with the current global-user scope remaining the default for backward compatibility:

by_user: "60/m"
user_scope: "server"   # new; default: "global" (existing behaviour)

Acceptance criteria

Scenario: Exhausting one MCP server does not consume another server's quota
  Given RateLimiterPlugin is configured with user_scope: server and by_user: X/m
  When User-1 invokes Tool-A on MCP-A X times within the current minute
  Then the next invocation by User-1 on MCP-A is rejected with RATE_LIMIT
  When User-1 invokes Tool-B on MCP-B during that same minute
  Then the MCP-B invocation succeeds

Scenario: Backward-compatible default
  Given RateLimiterPlugin is configured WITHOUT user_scope (or user_scope: global)
  Then behaviour is identical to cpex-rate-limiter <= 0.1.8

Implementation notes

  • Key on the catalog UUID (not name/slug) to avoid quota resets on server rename.
  • Document that by_user: X/m with user_scope: server means X/m per server, so a user with access to N servers has an effective ceiling of N×X/m; by_tenant becomes the primary global control.
  • At rollout, changing the Redis key shape orphans all in-flight fixed-window counters. Consider reading both key shapes for one window length during migration, or document the cut-over impact clearly.
  • The context_prefix parameter on RateLimiterEngine.check() handles tenant/team namespace isolation; server_id is a separate, orthogonal dimension.
  • Both check() and check_async() (sync/memory and async/Redis paths) need the new parameter.

Affected components

  • plugins/rust/python-package/cpex-rate-limiter/ — Rust engine (RateLimiterEngine, EngineConfig), Python shim (RateLimiterPluginCore), and stub (.pyi)
  • plugin-manifest.yaml — add user_scope to default_configs
  • Tests covering the new key shape (memory and Redis backends) and backward-compatibility

References

  • IBM/mcp-context-forge#6230 — gateway-side feature request with full acceptance criteria and operator notes
Dominant language
Rust
Stars
10
Forks
1
Avg merge
3d 19h
Merged PRs (30d)
7

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from IBM/cpex-plugins

All issues in IBM/cpex-plugins

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.