Skip to content

feat(mcp): allow skipping MCP host-tool registration in non-MCP processes - #43842

Open
eschutho wants to merge 1 commit into
apache:masterfrom
eschutho:fix/gate-mcp-host-tools-off-workers
Open

feat(mcp): allow skipping MCP host-tool registration in non-MCP processes#43842
eschutho wants to merge 1 commit into
apache:masterfrom
eschutho:fix/gate-mcp-host-tools-off-workers

Conversation

@eschutho

@eschutho eschutho commented Sep 4, 2026

Copy link
Copy Markdown
Member

SUMMARY

init_core_dependencies() calls initialize_core_mcp_dependencies() in every process that
builds the Flask app — including Celery workers, which never serve MCP. Registering the MCP host
tools imports the MCP service app, which carries a real per-process memory cost. On high-concurrency
prefork worker pools this import can push workers over their memory limit and OOM them at boot.

This adds a CORE_MCP_HOST_TOOLS_ENABLED config flag (default True, so no behavior change) and
gates the initialize_core_mcp_dependencies() call on it. Deployments can set it to False for
processes that never serve MCP (e.g. Celery workers) to avoid importing the MCP stack where it is
unused.

CHANGES

  • superset/config.py: new CORE_MCP_HOST_TOOLS_ENABLED = True flag with docstring.
  • superset/initialization/__init__.py: gate initialize_core_mcp_dependencies() on
    self.config.get("CORE_MCP_HOST_TOOLS_ENABLED", True).

TESTING INSTRUCTIONS

  • Default (flag True): the web app and standalone MCP service register host tools exactly as before.
  • Set CORE_MCP_HOST_TOOLS_ENABLED = False in a worker's config: the worker boots without importing
    the MCP host-tool stack; web / MCP processes are unaffected.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
  • Migration is atomic, supports rollback & is backwards-compatible
  • Confirm DB migration upgrade and downgrade tested
  • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

…sses

init_core_dependencies() calls initialize_core_mcp_dependencies() in every process that
builds the app, including Celery workers that never serve MCP. Importing the MCP service
app to register host tools has a real per-process memory cost that can OOM high-concurrency
prefork worker pools on boot.

Add a CORE_MCP_HOST_TOOLS_ENABLED config flag (default True, no behavior change) and gate
the call on it, so deployments can turn host-tool registration off for processes that do
not serve MCP.

Signed-off-by: Elizabeth Thompson <elizabeth@preset.io>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #31e528

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset/config.py - 2
    • Undocumented config option · Line 546-546
      This new config option is user-facing (deployments can set it in superset_config.py) but isn't listed in the MCP configuration reference in `docs/admin_docs/configuration/mcp-server.mdx`, which documents all other MCP settings. Per AGENTS.md, docs should be updated for user-facing changes. Consider adding a row for `CORE_MCP_HOST_TOOLS_ENABLED`.
    • Naming inconsistency · Line 546-546
      All other MCP settings use the `MCP_` prefix (`MCP_AUTH_ENABLED`, `MCP_RBAC_ENABLED`, etc.) and live in `mcp_service/mcp_config.py`. This new flag uses a `CORE_` prefix in `config.py`, which is inconsistent with the related API family and may confuse operators searching for MCP options. Align the name or note the intentional distinction.
Review Details
  • Files reviewed - 2 · Commit Range: fedcb64..fedcb64
    • superset/config.py
    • superset/initialization/__init__.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +565 to +566
if self.config.get("CORE_MCP_HOST_TOOLS_ENABLED", True):
initialize_core_mcp_dependencies()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: With this flag false, the abstract tool and prompt decorators remain active, so extensions using them raise NotImplementedError and are skipped during extension loading. [api mismatch]

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/initialization/__init__.py
**Line:** 565:566
**Comment:**
	*Api Mismatch: With this flag false, the abstract `tool` and `prompt` decorators remain active, so extensions using them raise `NotImplementedError` and are skipped during extension loading.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agreed—the disabled path leaves the Core decorators as raising stubs, so an extension entrypoint using @tool or @prompt can fail before its non-MCP contributions, including Celery tasks, are registered. Should decorator replacement be kept independent of host-tool registration?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes. Decorator replacement should be independent of host-tool registration.

CORE_MCP_HOST_TOOLS_ENABLED=False should prevent importing/registering the MCP host-tool stack, but it must still install the functional @tool and @prompt decorators before extensions load. Otherwise an extension that declares MCP metadata can raise NotImplementedError and prevent unrelated contributions, such as Celery tasks, from being registered.

A minimal structure would be:

initialize_core_mcp_decorators()

if self.config.get("CORE_MCP_HOST_TOOLS_ENABLED", True):
    initialize_core_mcp_host_tools()

This likely requires splitting initialize_core_mcp_dependencies() into a lightweight decorator-initialization step and a host-tool registration step. The decorator path should avoid importing the MCP service application, preserving the worker memory benefit while maintaining extension-loading compatibility.

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. When CORE_MCP_HOST_TOOLS_ENABLED is set to False, initialize_core_mcp_dependencies() is skipped, but if other parts of the application still attempt to use the tool or prompt decorators (which are typically registered by that function), they will fail with a NotImplementedError because the underlying infrastructure was not initialized.

To resolve this, you should ensure that the decorators themselves are either conditionally registered or provide a safe fallback when the MCP stack is disabled. A common approach is to make the decorators no-ops or return a dummy implementation when the feature is disabled, rather than raising an error.

Would you like me to fetch all comments and validate the rest of the PR to implement a comprehensive fix?

superset/initialization/init.py

if self.config.get("CORE_MCP_HOST_TOOLS_ENABLED", True):
            initialize_core_mcp_dependencies()

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.89%. Comparing base (8d2b655) to head (fedcb64).
⚠️ Report is 2616 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master   #43842       +/-   ##
===========================================
+ Coverage   64.16%   75.89%   +11.73%     
===========================================
  Files        2591     2791      +200     
  Lines      138162   158943    +20781     
  Branches    32048    35283     +3235     
===========================================
+ Hits        88647   120634    +31987     
+ Misses      47986    35808    -12178     
- Partials     1529     2501      +972     
Flag Coverage Δ
hive 37.78% <100.00%> (-1.68%) ⬇️
mysql 57.50% <100.00%> (-1.66%) ⬇️
postgres 57.53% <100.00%> (-1.71%) ⬇️
presto 39.67% <100.00%> (-1.48%) ⬇️
python 83.86% <100.00%> (+23.19%) ⬆️
sqlite 57.23% <100.00%> (-1.65%) ⬇️
unit 74.34% <100.00%> (-25.66%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aminghadersohi aminghadersohi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM — approving.

The diagnosis and the shape of the fix both look right, and defaulting to True means this is a genuine no-op until a deployment opts out.

One thing I verified explicitly, because it looks wrong at a glance and I suspect it'll come up again in review: the from superset.core.mcp.core_mcp_injection import initialize_core_mcp_dependencies at the top of init_core_dependencies sits outside the new if, so at first read the import cost appears to be paid regardless of the flag — which would defeat the stated purpose. It isn't. core_mcp_injection's module-level imports are just logging, typing, and superset.extensions.context; the expensive work (import superset_core.mcp.decorators, from fastmcp.tools import Tool, and from superset.mcp_service import app) all happens inside initialize_core_mcp_dependencies(). So gating the call does skip the heavy imports, and the memory win is real. Might be worth a one-line comment saying so, purely to save the next reader the same detour.

One question rather than a change request: gating the call also skips the two decorator replacements —

superset_core.mcp.decorators.tool = create_tool_decorator
superset_core.mcp.decorators.prompt = create_prompt_decorator

so on a process with the flag off, superset_core.mcp.decorators.tool stays the abstract version. If any extension that gets loaded in a Celery worker applies @tool/@prompt at import time, it'll now hit the abstract decorator instead of the concrete one. I couldn't check what the abstract implementation does from here (superset_core isn't in my environment). If it's an inert stub, this is a non-issue; if it raises, it'd be worth either leaving the decorator swap ungated or noting the constraint in the config docstring. You'll know the worker-side extension set better than I do.

Two trivia, neither blocking:

  • The new constant lands between the feature-flag lifecycle comment block (# - stable: ... / # - deprecated: ...) and DEFAULT_FEATURE_FLAGS, so it reads as though it belongs to the feature-flag section. It isn't a feature flag — which is the right call, since this is a process-startup concern rather than a runtime toggle — so a few lines earlier or later would make that clearer.
  • - [x] Changes UI is ticked in the description, but this is backend-only.

# (the web app and the standalone MCP service). Deployments can disable
# this in processes that never serve MCP -- e.g. Celery workers -- to
# avoid importing the MCP stack where it is unused.
if self.config.get("CORE_MCP_HOST_TOOLS_ENABLED", True):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This opt-out still imports superset.core.mcp.core_mcp_injection before evaluating the flag. That module imports mcp.types, whose package initializer imports MCP client/server modules, so high-concurrency workers still load much of the stack this setting is meant to avoid. Could the injection import move inside the enabled branch?

# (the web app and the standalone MCP service). Deployments can disable
# this in processes that never serve MCP -- e.g. Celery workers -- to
# avoid importing the MCP stack where it is unused.
if self.config.get("CORE_MCP_HOST_TOOLS_ENABLED", True):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There is no regression coverage for the process-level opt-out. Could a focused initialization test set CORE_MCP_HOST_TOOLS_ENABLED=False and assert MCP registration/import is skipped while core API initialization still runs, alongside the default-enabled case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants