Commit 1a9ad7f
refactor(plugins): migrate in-tree plugins to PyPI packages (cpex-*) (#3965)
* refactor(plugins): replace in-tree rate_limiter with cpex-rate-limiter package
Remove the in-tree rate_limiter plugin and replace it with the
cpex-rate-limiter PyPI package, a compiled Rust extension providing
the same RateLimiterPlugin class with additional algorithms
(sliding-window, token-bucket) alongside the original fixed-window.
- Add cpex-rate-limiter>=0.0.2 as a [plugins] optional dependency
- Update Containerfile.lite to install the plugins extra
- Remove plugins/rate_limiter/ source directory
- Remove unit and integration tests that imported plugin internals
- Update all config files to use cpex_rate_limiter.RateLimiterPlugin
- Disable RateLimiterPlugin in test fixture config (package not
available in unit test environment)
- Update documentation to reflect the external package
Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* feat(rate-limiter): pluggable algorithms with Rust-backed execution engine, benchmarks, and validation (#3809)
* feat(rate-limiter): pluggable algorithms, tenant isolation fix, and scale load test
- Add pluggable algorithm strategy: fixed_window, sliding_window, token_bucket
- Add Redis backend for shared cross-instance rate limiting
- Fix tenant isolation: skip by_tenant when tenant_id is None
- Fix sliding window: sweep expired timestamps before counting
- Fix backend validation: restore _validate_config check
- Fix token bucket memory path: apply max(1,...) guard to reset timestamp
- Add Redis integration tests for all three algorithms
- Add direct regression tests for get_current_user tenant_id fallback
- Add scale load test with Redis memory timeline and live algorithm detection
- Add RL_PACE_MULTIPLIER for near-limit pace testing and boundary burst detection
- Remove redundant algorithm locustfile; scale file is canonical
- Correct stale comments and README limitations
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
* feat(rate-limiter): add Rust-backed engine, check() API, benchmarks, and validation
- Rust-backed sliding window engine with pyo3-log integration
- check() API with tenant propagation, sweep/retry-after support
- Eliminate redundant ZRANGE in sliding window Lua script
- Fix detect-secrets baseline for rate limiter load tests
- Clarify memory backend is single-instance only in docs
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
* chore: regenerate detect-secrets baseline after rebase
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
* refactor(rate-limiter): review fixes, Redis hardening, key-format parity tests
- Extract _dispatch_hook() shared by prompt_pre_fetch and tool_pre_invoke,
reducing each hook to a single-line wrapper
- Elevate Redis val_i64/val_f64 parse-error logging from warn to error so
silent fail-open degradation surfaces in operator dashboards
- Clamp sliding-window reset_timestamp with .max(1) so it is always strictly
in the future even when the oldest entry expires in < 1 s
- Add 5 s tokio::time::timeout around Redis connection establishment to
prevent indefinite blocking on network partition
- Replace silent except-pass in EVALSHA SHA tracking with logger.debug
- Document dual Lua-script invariant (rolling-upgrade key-format parity)
in both Python RedisBackend docstring and Rust redis_backend.rs header
- Add 7 parametrized test_redis_key_format_parity_* tests validating that
Python and Rust produce identical Redis keys for the same inputs
- Revert unrelated .pyi stub changes for encoded_exfil_detection, pii_filter,
retry_with_backoff, and secrets_detection
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: strip trailing whitespace in pyi stubs, remove accidental .claude/ralph-loop.local.md
- Remove plugins_rust/rate_limiter/.claude/ralph-loop.local.md which
was accidentally committed — this is a local Claude Code loop state
file and should never have been checked in.
- Fix trailing whitespace in plugins_rust/rate_limiter/python/
rate_limiter_rust/__init__.pyi docstrings to pass pre-commit hooks.
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
* chore: regenerate detect-secrets baseline for new exfil test strings
Update .secrets.baseline after adding test_extra_sensitive_keywords
in plugins_rust/encoded_exfil_detection/src/lib.rs:969 which contains
a fake credential string that triggers the Secret Keyword detector.
All new entries are false positives (test data).
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
* chore: audit new detect-secrets baseline entries as false positives
The baseline regeneration reset is_secret to null for entries whose
line numbers shifted. Mark all 17 unaudited entries as is_secret=false
(test data, example configs, fake credentials) to pass the
--fail-on-unaudited pre-commit check.
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
---------
Signed-off-by: Pratik Gandhi <gandhipratik203@gmail.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* feat(discovery): add automatic tool discovery with hot/cold classification (#3839)
Implement automatic tool discovery for upstream MCP servers via
usage-aware adaptive polling. The gateway can now continuously
synchronise tool lists from registered servers without manual
intervention.
Server classification (hot/cold):
- Classify servers based on MCP session pool usage patterns
- Hot servers (top 20% by recent usage): polled at 1x base interval
- Cold servers (remaining 80%): polled at 3x base interval
- Classification is deterministic: sorted by recency, active sessions,
use count, and URL for tie-breaking
- Leader election via Redis with TTL renewal for multi-worker
coordination
- Falls back to local-only operation without Redis
Integration with GatewayService:
- Health checks respect hot/cold classification intervals
- Auto-refresh of tools/resources/prompts respects classification
- Fail-open on classification errors (poll anyway)
- Poll timestamps tracked via Redis with TTL expiry
- Uses base gateway URL (pre-auth) for classification lookups to
avoid leaking query-param auth secrets to Redis
Configuration:
- AUTO_REFRESH_SERVERS=true enables automatic tool sync (default: false)
- GATEWAY_AUTO_REFRESH_INTERVAL=300 sets base polling interval
- HOT_COLD_CLASSIFICATION_ENABLED=false (opt-in, requires Redis)
Includes comprehensive tests with 100% coverage on the new
ServerClassificationService and integration tests for the
GatewayService hot/cold polling paths.
Closes #3734
Signed-off-by: Lang-Akshay <akshay.shinde26@ibm.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* refactor(plugins): replace in-tree rate_limiter with cpex-rate-limiter package
Remove the in-tree rate_limiter plugin and replace it with the
cpex-rate-limiter PyPI package, a compiled Rust extension providing
the same RateLimiterPlugin class with additional algorithms
(sliding-window, token-bucket) alongside the original fixed-window.
- Add cpex-rate-limiter>=0.0.2 as a [plugins] optional dependency
- Update Containerfile.lite to install the plugins extra
- Remove plugins/rate_limiter/ source directory
- Remove unit and integration tests that imported plugin internals
- Update all config files to use cpex_rate_limiter.RateLimiterPlugin
- Disable RateLimiterPlugin in test fixture config (package not
available in unit test environment)
- Update documentation to reflect the external package
Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* refactor(plugins): update build, CI, and docs for PyPI plugin migration
Remove all plugins_rust/ build infrastructure and update references
across Containerfiles, Makefile, CI workflows, pre-commit configs,
CODEOWNERS, and documentation to reflect that plugins are now
distributed as PyPI packages (cpex-*) via the [plugins] optional extra.
- Remove Rust plugin builder stages from all Containerfiles
- Remove ~100 lines of rust-* plugin Makefile targets (keep mcp-runtime)
- Add --extra plugins to CI pytest workflow
- Add [plugins] extra to install-dev Makefile target
- Update tool_service.py import to use cpex_retry_with_backoff
- Update plugin kind paths in 7 doc files to cpex_pii_filter.*
- Clean up pre-commit, CODEOWNERS, MANIFEST.in, whitesource, .gitignore
Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* fix(plugins): address PR review findings on PyPI plugin migration
Round 1 (blockers + high):
- Restore exclude-newer = "10 days" in pyproject.toml; replace stale
langchain/requests pins with cpex-* per-package overrides anchored
to 2026-04-09 so the plugins resolve newer than the global window
- Guard cpex_retry_with_backoff import in tool_service.py with
try/except ImportError; falls back to (None, True) for the Python
pipeline when the optional [plugins] extra is not installed
- Delete orphaned .github/workflows/rust-plugins.yml and the
associated test cases in tests/unit/test_rust_plugins_workflow.py;
drop the workflow card from docs/docs/architecture/explorer.html
- Delete orphaned docs/docs/using/plugins/rust-plugins.md and remove
it from docs/docs/using/plugins/.pages mkdocs nav
- Harden docker-entrypoint.sh install_plugin_requirements:
canonicalize /app and the resolved requirements path with
readlink -f and require the path to live under /app/, log
non-comment lines from the requirements file before pip runs,
and skip cleanly on validation failure
- Delete PLUGIN-MIGRATION-PLAN.md (one-time planning doc)
- Add COPY plugins/requirements.txt to Containerfile.scratch (the
layered Containerfile.lite already had it; the broad COPY . in
Containerfile already includes it)
Round 2 (medium + low):
- Bump cpex-* version pin floors in pyproject.toml [plugins] to
match resolved versions in uv.lock (cpex-rate-limiter>=0.0.3,
cpex-encoded-exfil-detection>=0.2.0, cpex-pii-filter>=0.2.0,
cpex-url-reputation>=0.1.1)
- Add Prerequisites section to tests/performance/PLUGIN_PROFILING.md
documenting the [plugins] extra requirement
- Add Status: Partially superseded note to ADR-041 explaining that
plugins_rust/ was removed when in-tree Rust plugins migrated to
PyPI packages
- Document upgrade semantics in plugins/requirements.txt header
(pip without --upgrade skips already-satisfied constraints)
- Add importlib.util.find_spec() precheck to
tests/performance/test_plugins_performance.py main(); the script
now skips cleanly with an actionable message if any of the five
cpex packages referenced by the perf config are missing
- Rename tests/unit/test_rust_plugins_workflow.py to
test_go_toolchain_pinning.py to match its remaining contents
(Go workflow pin and Makefile toolchain assertion)
Follow-ups tracked in #4116 and
IBM/cpex-plugins#21 for the longer-term tool_service.py refactor
that will eliminate the cross-package import entirely.
Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* revert: restore tests changes from PR #3965
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* fix(ci): align plugin tests with PyPI migration
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* test: remove legacy plugin test skip infrastructure
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* test: align packaged plugin tests with rust shims
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* test: cover retry policy import path in tool service
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* fix: harden cpex plugin migration paths
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* test: cover retry policy parser branches
Signed-off-by: lucarlig <luca.carlig@ibm.com>
* test: cover plugin requirements entrypoint path
Signed-off-by: lucarlig <luca.carlig@ibm.com>
---------
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Pratik Gandhi <gandhipratik203@gmail.com>
Co-authored-by: Lang-Akshay <akshay.shinde26@ibm.com>
Co-authored-by: lucarlig <luca.carlig@ibm.com>1 parent 3c1ca03 commit 1a9ad7f
171 files changed
Lines changed: 1590 additions & 39592 deletions
File tree
- .claude/skills/pr-risk-scoring
- .github
- workflows
- docs/docs
- architecture
- adr
- development
- howto
- manage
- testing
- using/plugins
- llms
- mcpgateway
- services
- plugins_rust
- encoded_exfil_detection
- benches
- python/encoded_exfil_detection_rust
- src
- bin
- pii_filter
- benches
- benchmarks
- python/pii_filter_rust
- src
- bin
- rate_limiter
- benches
- python/rate_limiter_rust
- src
- bin
- retry_with_backoff
- python/retry_with_backoff_rust
- src
- bin
- secrets_detection
- benches
- examples
- python/secrets_detection_rust
- src
- bin
- url_reputation
- benches
- python/url_reputation_rust
- src
- filters
- plugins
- encoded_exfil_detection
- pii_filter
- rate_limiter
- retry_with_backoff
- secrets_detection
- url_reputation
- webhook_notification
- scripts
- tests
- integration
- performance
- plugins
- unit
- mcpgateway
- plugins
- fixtures/configs
- framework
- plugins
- pii_filter
- rate_limiter
- url_reputation
- services
- plugins
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| |||
167 | 168 | | |
168 | 169 | | |
169 | 170 | | |
170 | | - | |
| 171 | + | |
171 | 172 | | |
172 | 173 | | |
173 | 174 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
10 | 9 | | |
11 | 10 | | |
12 | 11 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
86 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
87 | 89 | | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | 90 | | |
105 | 91 | | |
106 | 92 | | |
107 | 93 | | |
108 | 94 | | |
109 | 95 | | |
110 | | - | |
| 96 | + | |
111 | 97 | | |
112 | 98 | | |
113 | 99 | | |
| |||
117 | 103 | | |
118 | 104 | | |
119 | 105 | | |
120 | | - | |
121 | | - | |
122 | 106 | | |
123 | 107 | | |
124 | 108 | | |
| |||
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
262 | 262 | | |
263 | 263 | | |
264 | 264 | | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | 265 | | |
271 | 266 | | |
272 | 267 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
| 39 | + | |
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| |||
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
251 | | - | |
252 | 251 | | |
253 | 252 | | |
254 | 253 | | |
| |||
0 commit comments