codeant-ai-for-open-source[bot] commented on code in PR #37216:
URL: https://github.com/apache/superset/pull/37216#discussion_r2700626709
##########
superset/mcp_service/server.py:
##########
@@ -17,13 +17,23 @@
"""
MCP server for Apache Superset
+
+Supports both single-pod (in-memory) and multi-pod (Redis) deployments.
+For multi-pod deployments, configure MCP_EVENT_STORE_CONFIG with Redis URL.
"""
import logging
import os
+from typing import Any
+
+import uvicorn
from superset.mcp_service.app import create_mcp_app, init_fastmcp_server
-from superset.mcp_service.mcp_config import get_mcp_factory_config
+from superset.mcp_service.mcp_config import (
+ get_mcp_factory_config,
+ MCP_STORE_CONFIG,
Review Comment:
**Suggestion:** Wrong configuration symbol imported: code imports
`MCP_STORE_CONFIG` but the new setting introduced in PR is
`MCP_EVENT_STORE_CONFIG`; this will cause a NameError or import mismatch if
only the new symbol exists. Import the correct name exported by the config
module. [possible bug]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ MCP service fails starting due ImportError on config import.
- ⚠️ CI jobs importing server will error immediately.
- ❌ Any runtime path that imports server is blocked.
```
</details>
```suggestion
MCP_EVENT_STORE_CONFIG,
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Start the Superset process or otherwise import
`superset.mcp_service.server` which
executes the import block at `superset/mcp_service/server.py:32-35`.
2. Python tries to resolve `MCP_STORE_CONFIG` from
`superset.mcp_service.mcp_config`; if
the PR/other changes only define `MCP_EVENT_STORE_CONFIG`, this import
raises ImportError
at module import time.
3. The ImportError aborts import, preventing any use of
`create_event_store()` or
`run_server()` (the module never loads).
4. This is not a hypothetical mismatch — the PR description names
`MCP_EVENT_STORE_CONFIG`, so keeping `MCP_STORE_CONFIG` is a concrete
mismatch with the
introduced symbol.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/server.py
**Line:** 34:34
**Comment:**
*Possible Bug: Wrong configuration symbol imported: code imports
`MCP_STORE_CONFIG` but the new setting introduced in PR is
`MCP_EVENT_STORE_CONFIG`; this will cause a NameError or import mismatch if
only the new symbol exists. Import the correct name exported by the config
module.
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.
```
</details>
##########
superset/mcp_service/server.py:
##########
@@ -55,21 +65,79 @@ def configure_logging(debug: bool = False) -> None:
logging.info("🔍 SQL Debug logging enabled")
+def create_event_store(config: dict[str, Any] | None = None) -> Any | None:
+ """
+ Create an EventStore for MCP session management.
+
+ For multi-pod deployments, uses Redis-backed storage to share session state
+ across pods. For single-pod deployments, returns None (uses in-memory).
+
+ Args:
+ config: Optional config dict. If None, reads from MCP_STORE_CONFIG.
+
+ Returns:
+ EventStore instance if Redis URL is configured, None otherwise.
+ """
+ if config is None:
+ config = MCP_STORE_CONFIG
+
+ redis_url = config.get("CACHE_REDIS_URL")
+ if not redis_url:
+ logging.info("EventStore: Using in-memory storage (single-pod mode)")
+ return None
+
+ try:
+ from fastmcp.server.event_store import EventStore
+
+ # Reuse _create_redis_store with wrap=False for raw RedisStore
+ redis_store = _create_redis_store(config, wrap=False)
+ if redis_store is None:
+ logging.warning("Failed to create Redis store, falling back to
in-memory")
+ return None
+
+ # Create EventStore with Redis backend
+ event_store = EventStore(
+ storage=redis_store,
+ max_events_per_stream=config.get("event_store_max_events", 100),
+ ttl=config.get("event_store_ttl", 3600),
Review Comment:
**Suggestion:** Configuration key mismatch: `create_event_store` reads
`CACHE_REDIS_URL` and expects `event_store_max_events`/`event_store_ttl`, but
the PR and examples use `redis_url`, `max_events_per_stream`, and `ttl`; this
will prevent Redis from being detected/configured and produce incorrect
EventStore parameters. Use the canonical MCP event-store keys. [logic error]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ Multi‑pod Redis sessions not used despite configuration.
- ❌ Session sharing across pods broken in Kubernetes.
- ⚠️ EventStore limits not applied from config.
```
</details>
```suggestion
redis_url = config.get("redis_url")
if not redis_url:
logging.info("EventStore: Using in-memory storage (single-pod mode)")
return None
try:
from fastmcp.server.event_store import EventStore
# Reuse _create_redis_store with wrap=False for raw RedisStore
redis_store = _create_redis_store(config, wrap=False)
if redis_store is None:
logging.warning("Failed to create Redis store, falling back to
in-memory")
return None
# Create EventStore with Redis backend (use canonical keys from
MCP_EVENT_STORE_CONFIG)
event_store = EventStore(
storage=redis_store,
max_events_per_stream=config.get("max_events_per_stream", 100),
ttl=config.get("ttl", 3600),
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Configure MCP event store using the example in the PR (set
`MCP_EVENT_STORE_CONFIG`
with keys `redis_url`, `max_events_per_stream`, `ttl`) in
`superset/mcp_service/mcp_config.py`.
2. Start the MCP service (call `run_server()`), which calls
`create_event_store()` at
`superset/mcp_service/server.py` and sets `config = MCP_STORE_CONFIG` when
`event_store_config` is None (`server.py:82` then uses keys at
`server.py:84`).
3. `create_event_store()` executes `config.get("CACHE_REDIS_URL")` at
`superset/mcp_service/server.py:84`; because the configured key is
`redis_url`, this
returns None.
4. The function logs "Using in-memory storage" and returns None, causing the
server to run
in single‑pod in‑memory mode despite Redis being configured.
5. Additionally, even if redis store is created, the parameters passed are
read from
`event_store_max_events` / `event_store_ttl` (lines `server.py:101-103`)
instead of the
PR's `max_events_per_stream` / `ttl`, so desired EventStore limits are
ignored.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/server.py
**Line:** 84:102
**Comment:**
*Logic Error: Configuration key mismatch: `create_event_store` reads
`CACHE_REDIS_URL` and expects `event_store_max_events`/`event_store_ttl`, but
the PR and examples use `redis_url`, `max_events_per_stream`, and `ttl`; this
will prevent Redis from being detected/configured and produce incorrect
EventStore parameters. Use the canonical MCP event-store keys.
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.
```
</details>
##########
superset/mcp_service/storage.py:
##########
@@ -109,15 +115,51 @@ def _create_redis_store(
return None
try:
+ # Parse URL to handle SSL properly
+ parsed = urlparse(redis_url)
+ use_ssl = parsed.scheme == "rediss"
+
+ # Build clean URL without query params that may cause issues
+ clean_url = f"{parsed.scheme}://"
+ if parsed.username or parsed.password:
+ auth = parsed.username if parsed.username else ""
+ if parsed.password:
+ auth += f":{parsed.password}"
+ clean_url += f"{auth}@"
+ clean_url += f"{parsed.hostname or 'localhost'}"
+ clean_url += f":{parsed.port or 6379}"
+ clean_url += f"/{parsed.path.strip('/') or '0'}"
+
+ # Create Redis store with SSL support for cloud deployments
+ # Note: RedisStore uses redis.asyncio under the hood which handles
+ # SSL automatically via the rediss:// URL scheme
+ if use_ssl:
+ # For ElastiCache with self-signed certs, append ssl_cert_reqs
param
+ # redis.asyncio understands this as a query param in the URL
+ ssl_url = f"{clean_url}?ssl_cert_reqs=none"
+ redis_store = RedisStore(url=ssl_url)
+ logger.info("Created RedisStore with SSL at %s", parsed.hostname)
+ else:
+ redis_store = RedisStore(url=clean_url)
Review Comment:
**Suggestion:** Security issue: appending "ssl_cert_reqs=none" to the Redis
URL disables TLS certificate validation and allows MITM attacks; do not inject
an insecure parameter—use the original URL and let the Redis client/RedisStore
handle SSL, or provide a properly configured SSLContext instead. [security]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ EventStore sessions vulnerable to network MITM.
- ❌ Redis connections accept invalid certificates.
- ⚠️ Multi-pod session sharing compromised in cloud.
```
</details>
```suggestion
# Create Redis store and let the underlying client handle SSL from
the URL;
# avoid disabling certificate verification by appending insecure
query params.
redis_store = RedisStore(url=redis_url)
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Enable MCP Redis storage in Flask config: set
MCP_STORE_CONFIG['enabled']=True and
MCP_STORE_CONFIG['CACHE_REDIS_URL']='rediss://example.amazonaws.com:6379/1'
in your Flask
app configuration (superset/mcp_service/storage.py calls into this at
_get_store:
storage.py:67-75).
2. Call get_mcp_store(prefix=...) from application code
(superset/mcp_service/storage.py:get_mcp_store -> inner _get_store which
calls
_create_redis_store at storage.py:85-166).
3. Inside _create_redis_store (storage.py:85-166), the code detects rediss
scheme and
executes the branch at storage.py:136-141 which constructs ssl_url by
appending
?ssl_cert_reqs=none and passes it to RedisStore(url=ssl_url)
(storage.py:139-140).
4. Observe that RedisStore is created with a URL that disables TLS
certificate validation,
effectively allowing MITM attacks between the process and the Redis endpoint.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/storage.py
**Line:** 136:143
**Comment:**
*Security: Security issue: appending "ssl_cert_reqs=none" to the Redis
URL disables TLS certificate validation and allows MITM attacks; do not inject
an insecure parameter—use the original URL and let the Redis client/RedisStore
handle SSL, or provide a properly configured SSLContext instead.
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.
```
</details>
##########
superset/mcp_service/server.py:
##########
@@ -55,21 +65,79 @@ def configure_logging(debug: bool = False) -> None:
logging.info("🔍 SQL Debug logging enabled")
+def create_event_store(config: dict[str, Any] | None = None) -> Any | None:
+ """
+ Create an EventStore for MCP session management.
+
+ For multi-pod deployments, uses Redis-backed storage to share session state
+ across pods. For single-pod deployments, returns None (uses in-memory).
+
+ Args:
+ config: Optional config dict. If None, reads from MCP_STORE_CONFIG.
+
+ Returns:
+ EventStore instance if Redis URL is configured, None otherwise.
+ """
+ if config is None:
+ config = MCP_STORE_CONFIG
Review Comment:
**Suggestion:** Default config variable name mismatch: `create_event_store`
falls back to `MCP_STORE_CONFIG` when `config is None`, but the PR introduced
`MCP_EVENT_STORE_CONFIG`; update the fallback to use the correct exported
config variable name to avoid NameError. [possible bug]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ Default EventStore config lookup fails on service start.
- ⚠️ Multi‑pod configuration ignored due to wrong fallback.
- ❌ Service startup may raise NameError.
```
</details>
```suggestion
config = MCP_EVENT_STORE_CONFIG
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Do not pass `event_store_config` to `run_server()` so
`create_event_store()` is called
with `config=None` (this is the default path in
`superset/mcp_service/server.py`).
2. `create_event_store()` executes `if config is None:` at `server.py:81`
and then `config
= MCP_STORE_CONFIG` at `server.py:82`.
3. If the actual configuration symbol in
`superset/mcp_service/mcp_config.py` is
`MCP_EVENT_STORE_CONFIG` (as the PR describes), `MCP_STORE_CONFIG` may be
undefined,
causing NameError at import time or leading to incorrect fallback behavior.
4. Because the PR introduced `MCP_EVENT_STORE_CONFIG`, updating the fallback
avoids a
concrete mismatch between exported config name and the one used here.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/server.py
**Line:** 82:82
**Comment:**
*Possible Bug: Default config variable name mismatch:
`create_event_store` falls back to `MCP_STORE_CONFIG` when `config is None`,
but the PR introduced `MCP_EVENT_STORE_CONFIG`; update the fallback to use the
correct exported config variable name to avoid NameError.
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.
```
</details>
##########
superset/mcp_service/storage.py:
##########
@@ -109,15 +115,51 @@ def _create_redis_store(
return None
try:
+ # Parse URL to handle SSL properly
+ parsed = urlparse(redis_url)
+ use_ssl = parsed.scheme == "rediss"
+
+ # Build clean URL without query params that may cause issues
+ clean_url = f"{parsed.scheme}://"
+ if parsed.username or parsed.password:
+ auth = parsed.username if parsed.username else ""
+ if parsed.password:
+ auth += f":{parsed.password}"
+ clean_url += f"{auth}@"
+ clean_url += f"{parsed.hostname or 'localhost'}"
+ clean_url += f":{parsed.port or 6379}"
+ clean_url += f"/{parsed.path.strip('/') or '0'}"
+
+ # Create Redis store with SSL support for cloud deployments
+ # Note: RedisStore uses redis.asyncio under the hood which handles
+ # SSL automatically via the rediss:// URL scheme
+ if use_ssl:
+ # For ElastiCache with self-signed certs, append ssl_cert_reqs
param
+ # redis.asyncio understands this as a query param in the URL
+ ssl_url = f"{clean_url}?ssl_cert_reqs=none"
+ redis_store = RedisStore(url=ssl_url)
+ logger.info("Created RedisStore with SSL at %s", parsed.hostname)
+ else:
+ redis_store = RedisStore(url=clean_url)
+ logger.info("Created RedisStore at %s", parsed.hostname)
+
+ # Return raw store if wrapping not requested
+ if not wrap:
+ return redis_store
+
+ # Wrap with prefix
+ if prefix is None:
+ logger.error("prefix is required when wrap=True")
+ return None
+
wrapper_type = store_config.get("WRAPPER_TYPE")
if not wrapper_type:
logger.error("MCP store WRAPPER_TYPE not configured")
return None
wrapper_class = _import_wrapper_class(wrapper_type)
- redis_store = RedisStore(url=redis_url)
store = wrapper_class(key_value=redis_store, prefix=prefix)
Review Comment:
**Suggestion:** If `prefix` is a callable (allowed by the public API),
passing it directly into the wrapper may be incorrect; evaluate callable
prefixes before handing them to the wrapper so the wrapper always receives a
string/prefix value. [logic error]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ Cache keys prefixed incorrectly, breaking lookups.
- ❌ EventStore streams mis-scoped across features.
- ⚠️ Docstring examples (callable prefixes) produce bugs.
```
</details>
```suggestion
if callable(prefix):
final_prefix = prefix()
else:
final_prefix = prefix
wrapper_class = _import_wrapper_class(wrapper_type)
store = wrapper_class(key_value=redis_store, prefix=final_prefix)
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Use the documented example in storage.py's get_mcp_store: pass a callable
prefix (e.g.,
cache_prefix_lambda) into get_mcp_store (function entry and inner _get_store
at
storage.py:67-75).
2. get_mcp_store calls _create_redis_store (storage.py:85-166) and reaches
wrapper
instantiation at storage.py:160-163 where prefix is forwarded unchanged into
wrapper_class(..., prefix=prefix).
3. Because the code does not evaluate callables, the wrapper receives a
callable object
instead of a string; wrappers expecting strings use that callable as a
literal prefix
leading to incorrect key names and lookup failures.
4. Observe broken cache keys or EventStore streams using the callable
object's repr
instead of the intended string prefix.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/storage.py
**Line:** 160:161
**Comment:**
*Logic Error: If `prefix` is a callable (allowed by the public API),
passing it directly into the wrapper may be incorrect; evaluate callable
prefixes before handing them to the wrapper so the wrapper always receives a
string/prefix value.
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.
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]