Skip to content

refactor(plugins): decouple plugin framework from gateway observability service#3140

Merged
jonpspri merged 6 commits intomainfrom
refactor/plugins_observability
Feb 24, 2026
Merged

refactor(plugins): decouple plugin framework from gateway observability service#3140
jonpspri merged 6 commits intomainfrom
refactor/plugins_observability

Conversation

@crivetimihai
Copy link
Copy Markdown
Member

Note: This PR was re-created from #2827 due to repository maintenance. Your code and branch are intact. @araujof please verify everything looks good.

Description

Removes the plugin framework's direct dependency on mcpgateway.db.SessionLocal and mcpgateway.services.observability_service by introducing a protocol-based observability abstraction with dependency injection.

Closes: #2828

Problem

The plugin framework's _execute_with_timeout method imported SessionLocal and ObservabilityService directly from the gateway, creating a tight coupling that prevented the framework from operating standalone. This also required the plugin framework to manage database sessions for tracing — a concern that belongs to the host application.

Solution

  • Introduced ObservabilityProvider protocol and NullObservability no-op default in a new observability.py module
  • Added a current_trace_id ContextVar owned by the plugin framework (replacing the one imported from the gateway's observability service)
  • PluginExecutor and PluginManager now accept an optional observability parameter via constructor injection
  • get_plugin_manager() forwards the observability provider to the manager
  • The host application (mcpgateway) can inject its own implementation at startup; the framework operates without tracing when none is provided

Dependency direction (before → after)

# Before: circular
plugin framework ──imports──> mcpgateway.db, mcpgateway.services.observability_service

# After: clean inversion
mcpgateway ──injects──> plugin framework (ObservabilityProvider)

Changes

File Change
mcpgateway/plugins/framework/observability.py NewObservabilityProvider protocol, NullObservability default, current_trace_id ContextVar
mcpgateway/plugins/framework/manager.py PluginExecutor and PluginManager accept observability param; _execute_with_timeout uses injected provider instead of direct gateway imports
mcpgateway/plugins/framework/__init__.py get_plugin_manager() accepts observability param; ObservabilityProvider added to __all__
tests/unit/.../test_observability.py New — 5 tests: DI injection with tracing, no trace_id, no provider, NullObservability, executor injection
tests/unit/.../test_manager_coverage.py Updated TestExecuteWithTimeout to test against injected provider instead of mocking mcpgateway.db/mcpgateway.services

Additional Changes

A few tests in tests/unit/../test_http_auth_integration.py were failing on dev environment when running the coverage target. Solution: Reset Starlette's cached middleware_stack after injecting the mock plugin manager so the middleware chain is rebuilt with the mock, and restore it afterward to avoid side effects on other tests.

Test plan

  • All unit tests pass
  • All doctests pass
  • New observability tests verify: span creation/completion with injected provider, graceful no-op without trace_id, graceful no-op without provider, provider failure fallback

@crivetimihai crivetimihai added this to the Release 1.0.0-GA milestone Feb 24, 2026
@crivetimihai crivetimihai added enhancement New feature or request plugins SHOULD P2: Important but not vital; high-value items that are not crucial for the immediate release labels Feb 24, 2026
araujof and others added 6 commits February 24, 2026 14:41
…ion for observability service

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
- Eliminate double plugin execution on failure by restructuring
  _execute_with_timeout to separate observability from plugin execution
- Close orphaned spans with status="error" when plugin hooks raise
- Fix docstring typo in ObservabilityMiddleware
- Move ObservabilityServiceAdapter import inside conditional block
- Document ContextVar bridge requirement on both definitions

Signed-off-by: Jonathan Springer <jps@s390x.com>
Cover error and edge-case paths in plugin executor observability
spans and get_plugin_manager initialization with plugins enabled.

Signed-off-by: Jonathan Springer <jps@s390x.com>
@jonpspri jonpspri force-pushed the refactor/plugins_observability branch from 7c64397 to dde843a Compare February 24, 2026 14:43
Copy link
Copy Markdown
Collaborator

@jonpspri jonpspri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean refactor — protocol-based DI correctly decouples plugin framework from gateway observability internals, and fixes the latent double-execution bug in the old catch-all fallback. Session lifecycle, Borg singleton interaction, and ContextVar bridge are all sound. Test coverage is thorough.

Minor suggestions (non-blocking):

  • Consider exporting or removing NullObservability (currently dead code in production).
  • A defensive if self.observability: guard before end_span in the error/success paths of _execute_with_timeout would make the null-safety explicit rather than relying on the surrounding except.
  • The duplicated try/except/finally session boilerplate in the adapter could be collapsed into a helper.

@jonpspri jonpspri merged commit 6000884 into main Feb 24, 2026
6 of 49 checks passed
@jonpspri jonpspri deleted the refactor/plugins_observability branch February 24, 2026 20:44
vishu-bh pushed a commit that referenced this pull request Feb 25, 2026
…ty service (#3140)

* refactor(plugins/framework): introduce protocol and dependency injection for observability service

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: fix lint issues

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* feat: add observability adapter for plugins

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* tests: coverage gaps

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: address review findings in observability refactor

- Eliminate double plugin execution on failure by restructuring
  _execute_with_timeout to separate observability from plugin execution
- Close orphaned spans with status="error" when plugin hooks raise
- Fix docstring typo in ObservabilityMiddleware
- Move ObservabilityServiceAdapter import inside conditional block
- Document ContextVar bridge requirement on both definitions

Signed-off-by: Jonathan Springer <jps@s390x.com>

* tests: achieve 100% diff coverage for observability refactor

Cover error and edge-case paths in plugin executor observability
spans and get_plugin_manager initialization with plugins enabled.

Signed-off-by: Jonathan Springer <jps@s390x.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Jonathan Springer <jps@s390x.com>
vishu-bh pushed a commit that referenced this pull request Feb 25, 2026
…ty service (#3140)

* refactor(plugins/framework): introduce protocol and dependency injection for observability service

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: fix lint issues

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* feat: add observability adapter for plugins

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* tests: coverage gaps

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: address review findings in observability refactor

- Eliminate double plugin execution on failure by restructuring
  _execute_with_timeout to separate observability from plugin execution
- Close orphaned spans with status="error" when plugin hooks raise
- Fix docstring typo in ObservabilityMiddleware
- Move ObservabilityServiceAdapter import inside conditional block
- Document ContextVar bridge requirement on both definitions

Signed-off-by: Jonathan Springer <jps@s390x.com>

* tests: achieve 100% diff coverage for observability refactor

Cover error and edge-case paths in plugin executor observability
spans and get_plugin_manager initialization with plugins enabled.

Signed-off-by: Jonathan Springer <jps@s390x.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Jonathan Springer <jps@s390x.com>
MohanLaksh pushed a commit that referenced this pull request Mar 12, 2026
…ty service (#3140)

* refactor(plugins/framework): introduce protocol and dependency injection for observability service

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: fix lint issues

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* feat: add observability adapter for plugins

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* tests: coverage gaps

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: address review findings in observability refactor

- Eliminate double plugin execution on failure by restructuring
  _execute_with_timeout to separate observability from plugin execution
- Close orphaned spans with status="error" when plugin hooks raise
- Fix docstring typo in ObservabilityMiddleware
- Move ObservabilityServiceAdapter import inside conditional block
- Document ContextVar bridge requirement on both definitions

Signed-off-by: Jonathan Springer <jps@s390x.com>

* tests: achieve 100% diff coverage for observability refactor

Cover error and edge-case paths in plugin executor observability
spans and get_plugin_manager initialization with plugins enabled.

Signed-off-by: Jonathan Springer <jps@s390x.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
Co-authored-by: Jonathan Springer <jps@s390x.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request plugins SHOULD P2: Important but not vital; high-value items that are not crucial for the immediate release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Remove observability service dependency from plugin framework

3 participants