Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion initialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ def initialize_mcp():
set = settings.get_settings()
async def initialize_mcp_async():
from python.helpers.mcp_handler import initialize_mcp as _initialize_mcp
return _initialize_mcp(set["mcp_servers"])
from python.helpers.secrets import get_default_secrets_manager
resolved = get_default_secrets_manager().replace_placeholders(set["mcp_servers"])
return _initialize_mcp(resolved)
return defer.DeferredTask().start_task(initialize_mcp_async)

def initialize_job_loop():
Expand Down
16 changes: 14 additions & 2 deletions python/helpers/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,12 @@ def create_streaming_filter(self) -> "StreamingSecretsFilter":
return StreamingSecretsFilter(self.load_secrets())

def replace_placeholders(self, text: str) -> str:
"""Replace secret placeholders with actual values"""
"""Replace secret placeholders with actual values.

Handles both literal §§secret(KEY) and JSON-escaped variants
(\\u00a7\\u00a7secret(KEY)) that appear when the placeholder is
inside a JSON-encoded string value stored in settings.
"""
if not text:
return text

Expand All @@ -277,7 +282,14 @@ def replacer(match):

raise RepairableException(error_msg)

return re.sub(self.PLACEHOLDER_PATTERN, replacer, text)
# First try the standard pattern with literal § characters
result = re.sub(self.PLACEHOLDER_PATTERN, replacer, text)
# Also handle JSON-escaped Unicode variants: \u00a7\u00a7secret(KEY)
# This occurs when §§secret() placeholders are inside JSON string values
# that are themselves stored as a JSON-encoded string in settings.
escaped_pattern = r"\\u00a7\\u00a7secret\(([A-Za-z_][A-Za-z0-9_]*)\)"
result = re.sub(escaped_pattern, replacer, result)
return result

def change_placeholders(self, text: str, new_format: str) -> str:
"""Substitute secret placeholders with a different placeholder format"""
Expand Down
5 changes: 5 additions & 0 deletions python/helpers/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ async def update_mcp_settings(mcp_servers: str):

mcp_config = MCPConfig.get_instance()
try:
# Resolve §§secret(KEY) placeholders in MCP server config before parsing.
# This allows API keys for external MCP servers to be stored securely
# in secrets.env rather than hardcoded in the MCP config JSON.
from python.helpers.secrets import get_default_secrets_manager
mcp_servers = get_default_secrets_manager().replace_placeholders(mcp_servers)
MCPConfig.update(mcp_servers)
except Exception as e:

Expand Down