Skip to content

[FEATURE]: Replace Internal Plugin Framework with CPEX (standalone plugin framework package) #3753

@araujof

Description

@araujof

Description

Replace the internal plugin framework (mcpgateway/plugins/framework/, mcpgateway/plugins/tools/, plugin_templates/) with the CPEX external package (cpex).

The implementation plan follows.

Phase 0: Preparation (no code changes)

  1. Install cpex==0.1.0.dev10 in a test venv and verify the public API surface matches current mcpgateway.plugins.framework exports.
  2. Run make test to establish green baseline.
  3. Run tests/performance/test_plugins_performance.py to capture baseline numbers.
  4. Confirm CPEX has: PluginMode.SEQUENTIAL, PluginMode.TRANSFORM, PluginMode.AUDIT, PluginMode.CONCURRENT, PluginMode.FIRE_AND_FORGET, PluginMode.DISABLED, and OnError.IGNORE.

Gate: All tests green. CPEX API verified.

Phase 1: Add CPEX dependency + compatibility shim

Goal: Add cpex and create re-export shims so all existing imports continue to work unchanged.

Steps

  1. pyproject.toml:

    • Add "cpex>=0.1.0.dev10" to dependencies
    • Add "cpex" to known_third_party in [tool.isort] and [tool.ruff.lint.isort]
  2. Replace mcpgateway/plugins/framework/ contents with thin shim files that re-export from cpex.framework. Every existing sub-module path (e.g., framework/models.py, framework/hooks/tools.py, framework/external/mcp/server.py) gets a 1-line from cpex.framework.<path> import * file.

  3. Keep get_plugin_manager() in mcpgateway/plugins/framework/__init__.py — it imports gateway-side policy.py and can't live in cpex. The shim __init__.py re-exports everything from cpex.framework plus defines get_plugin_manager().

  4. Replace mcpgateway/plugins/tools/ with shims re-exporting from cpex.tools.

  5. Update mcpgateway/plugins/policy.py import:

    • from mcpgateway.plugins.framework.hooks.policies import HookPayloadPolicyfrom cpex.framework.hooks.policies import HookPayloadPolicy

Gate: make test passes. make flake8 bandit pylint verify clean. Performance tests show no regression.

Phase 2: Migrate PluginMode enums

Goal: Replace old enum values with new CPEX modes throughout.

Mapping

Old New
ENFORCE SEQUENTIAL
PERMISSIVE TRANSFORM
ENFORCE_IGNORE_ERROR SEQUENTIAL + on_error: ignore
DISABLED DISABLED (unchanged)

Files to update

  • plugins/config.yaml: All mode values ("enforce""sequential", "permissive""transform", add on_error: ignore where applicable)
  • Plugin source files using PluginMode directly (e.g., plugins/resource_filter/resource_filter.py)
  • Test files referencing old enum values (~19 files in tests/unit/mcpgateway/plugins/)
  • Test fixture plugins in tests/unit/mcpgateway/plugins/fixtures/

Gate: make test passes. grep -rn 'ENFORCE\|PERMISSIVE\|ENFORCE_IGNORE_ERROR' --include='*.py' mcpgateway/ plugins/ tests/ returns zero hits (excluding comments/docs).

Phase 3: Migrate all imports to cpex

Goal: Replace from mcpgateway.plugins.framework with from cpex.framework everywhere so shims are no longer needed.

Sub-phase 3a: Gateway core (~11 files)

File Key imports
mcpgateway/main.py HttpHookType, PluginError, PluginManager, PluginViolationError
mcpgateway/config.py settings from framework
mcpgateway/auth.py get_plugin_manager, GlobalContext, HTTP payloads
mcpgateway/services/tool_service.py Hook types, payloads, constants
mcpgateway/services/prompt_service.py Hook types, payloads
mcpgateway/services/resource_service.py Hook types, payloads
mcpgateway/services/plugin_service.py PluginManager, PluginMode
mcpgateway/middleware/http_auth_middleware.py HTTP payloads
mcpgateway/middleware/rbac.py get_plugin_manager, HTTP auth payloads
mcpgateway/middleware/observability_middleware.py current_trace_id

Move get_plugin_manager() from mcpgateway/plugins/framework/__init__.py to mcpgateway/plugins/__init__.py. Update the ~5 callers to from mcpgateway.plugins import get_plugin_manager.

Sub-phase 3b: Plugin source files (~55 files)

Mechanical find-and-replace across all plugins/**/*.py:

  • from mcpgateway.plugins.frameworkfrom cpex.framework
  • from mcpgateway.plugins.toolsfrom cpex.tools

Sub-phase 3c: Plugin docs

Update plugins/README.md and plugins/AGENTS.md code examples.

Gate: make test, make autoflake isort black, make flake8 bandit pylint verify all clean. grep -rn 'from mcpgateway.plugins.framework' mcpgateway/ plugins/ returns zero.

Phase 4: Migrate tests — remove framework internals, add acceptance tests

Sub-phase 4a: Remove framework-internal tests

Delete tests/unit/mcpgateway/plugins/framework/ entirely (~24 test files + subdirs hooks/, loader/, external/). These tests now live in the CPEX repo.

Sub-phase 4b: Create acceptance tests

Create tests/acceptance/plugins/test_cpex_contract.py with:

  1. API surface tests — verify all symbols the gateway imports from cpex exist and are callable/instantiable.
  2. Behavioral contract tests — verify:
    • PluginManager accepts hook_policies parameter
    • PluginPayload is frozen
    • PluginMode has SEQUENTIAL, TRANSFORM, AUDIT, CONCURRENT, FIRE_AND_FORGET, DISABLED
    • OnError has IGNORE
    • HookPayloadPolicy can be constructed with writable_fields
    • ObservabilityProvider protocol has expected methods
  3. Serialization contract tests — verify payload models round-trip correctly.
  4. Settings compatibility — verify cpex reads PLUGINS_* env vars.

Sub-phase 4c: Update remaining test imports

  • tests/unit/mcpgateway/plugins/conftest.py
  • tests/unit/mcpgateway/plugins/fixtures/plugins/*.py (7 fixture files)
  • tests/unit/mcpgateway/plugins/plugins/ (30+ plugin test files)
  • Service tests (test_tool_service.py, test_prompt_service.py, test_resource_service.py, test_plugin_service.py)
  • Middleware tests (test_rbac.py, test_http_auth_*.py)
  • tests/unit/mcpgateway/test_main.py, test_auth.py
  • tests/unit/mcpgateway/plugins/tools/test_cli.py
  • tests/performance/test_plugins_performance.py
  • Integration tests referencing framework

Gate: make test passes. Performance tests pass. grep -rn 'from mcpgateway.plugins.framework' tests/ returns zero.

Phase 5: Remove shims, internal framework, and plugin_templates

Deletions

  • mcpgateway/plugins/framework/ — entire directory (48 files)
  • mcpgateway/plugins/tools/ — entire directory (3 files)
  • plugin_templates/ — entire directory (moved to CPEX)

Config updates

  • pyproject.toml: Update CLI entry point mcpplugins to point to cpex.tools.cli:main. Remove framework-related paths from tool configs.
  • .pre-commit-config.yaml & .pre-commit-lite.yaml: Remove plugin_templates/ from exclude patterns.
  • .github/workflows/lint.yml: Remove plugin_templates/ exclusions.
  • CLAUDE.md: Remove plugin_templates/ from project structure, note cpex dependency.
  • llms/plugins-llms.md: Update import paths.

Verify mcpgateway/plugins/__init__.py contains only get_plugin_manager() factory + re-exports.

Gate: make test, make autoflake isort black pre-commit, make flake8 bandit pylint verify all clean. python -c "from cpex.framework import PluginManager" works. Performance tests pass.

Phase 6: Documentation updates

Files to update (under docs/docs/using/plugins/)

  • index.md, plugins.md, lifecycle.md, http-auth-hooks.md, mtls.md, grpc-transport.md, unix-socket-transport.md, rust-plugins.md

Do NOT update

  • docs/docs/architecture/plugins.md (original specification)

Content changes

  • All from mcpgateway.plugins.frameworkfrom cpex.framework
  • Document new PluginMode values and execution order
  • Document OnError enum
  • Add links to CPEX repo
  • Remove plugin_templates/ references (bootstrap now via CPEX)
  • Update example config.yaml mode values

Gate: Docs build clean. No broken links.

Risk Mitigation

  • Shim-first approach (Phase 1): All existing code works before any imports change — key risk reduction.
  • Phase-per-commit: Each phase is independently committable and revertable.
  • Acceptance tests (Phase 4b): Catch CPEX incompatibilities on future version bumps.
  • Version pinning: Start with cpex>=0.1.0.dev10,<0.2.0 to avoid surprise breaks.
  • get_plugin_manager() stays gateway-side: It bridges cpex and policy.py — cannot live in cpex.

Key Files Reference

File Role
mcpgateway/plugins/framework/__init__.py Current facade + get_plugin_manager() singleton
mcpgateway/plugins/__init__.py Target home for get_plugin_manager()
mcpgateway/plugins/policy.py Gateway-specific hook payload policies (stays)
pyproject.toml Add cpex dep, update entry points
plugins/config.yaml Plugin config — update mode values
mcpgateway/services/tool_service.py Heaviest framework consumer (pattern for all services)
tests/unit/mcpgateway/plugins/conftest.py Test fixture setup

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafedesignArchitecture and DesignenhancementNew feature or requestplugins

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions