Add envvar overrides for mcp app (#606)

Needed for deployed MCP servers so that the Engine can override port,
host, and transport.
This commit is contained in:
Eric Gustin 2025-10-07 12:43:59 -07:00 committed by GitHub
parent ab93356e7c
commit 805ad2d888
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 89 additions and 0 deletions

View file

@ -6,6 +6,7 @@ Provides a clean, minimal API for building MCP servers with lazy initialization.
from __future__ import annotations from __future__ import annotations
import os
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Literal, ParamSpec, TypeVar from typing import Any, Callable, Literal, ParamSpec, TypeVar
@ -210,6 +211,8 @@ class MCPApp:
logger.info(f"Starting {self.name} v{self.version} with {len(self._catalog)} tools") logger.info(f"Starting {self.name} v{self.version} with {len(self._catalog)} tools")
host, port, transport = MCPApp._get_configuration_overrides(host, port, transport)
if transport in ["http", "streamable-http", "streamable"]: if transport in ["http", "streamable-http", "streamable"]:
run_arcade_mcp( run_arcade_mcp(
catalog=self._catalog, catalog=self._catalog,
@ -232,6 +235,37 @@ class MCPApp:
else: else:
raise ServerError(f"Invalid transport: {transport}") raise ServerError(f"Invalid transport: {transport}")
@staticmethod
def _get_configuration_overrides(
host: str, port: int, transport: TransportType
) -> tuple[str, int, TransportType]:
"""Get configuration overrides from environment variables."""
if envvar_transport := os.getenv("ARCADE_SERVER_TRANSPORT"):
transport = envvar_transport
logger.debug(
f"Using '{transport}' as transport from ARCADE_SERVER_TRANSPORT environment variable"
)
# host and port are only relevant for HTTP Streamable transport
if transport in ["http", "streamable-http", "streamable"]:
if envvar_host := os.getenv("ARCADE_SERVER_HOST"):
host = envvar_host
logger.debug(f"Using '{host}' as host from ARCADE_SERVER_HOST environment variable")
if envvar_port := os.getenv("ARCADE_SERVER_PORT"):
try:
port = int(envvar_port)
except ValueError:
logger.warning(
f"Invalid port: '{envvar_port}' from ARCADE_SERVER_PORT environment variable. Using default port {port}"
)
else:
logger.debug(
f"Using '{port}' as port from ARCADE_SERVER_PORT environment variable"
)
return host, port, transport
class _ToolsAPI: class _ToolsAPI:
"""Unified tools API for MCPApp (build-time and runtime).""" """Unified tools API for MCPApp (build-time and runtime)."""

View file

@ -179,3 +179,58 @@ class TestMCPApp:
# Test removing a resource at runtime # Test removing a resource at runtime
removed_resource = await mcp_app.resources.remove("file:///test.txt") removed_resource = await mcp_app.resources.remove("file:///test.txt")
assert removed_resource.uri == "file:///test.txt" assert removed_resource.uri == "file:///test.txt"
def test_get_configuration_overrides(self, monkeypatch):
"""Test configuration overrides from environment variables."""
# Ensure environment variables are clear at the start
monkeypatch.delenv("ARCADE_SERVER_TRANSPORT", raising=False)
monkeypatch.delenv("ARCADE_SERVER_HOST", raising=False)
monkeypatch.delenv("ARCADE_SERVER_PORT", raising=False)
# Test default values (no environment variables)
host, port, transport = MCPApp._get_configuration_overrides("127.0.0.1", 8000, "http")
assert host == "127.0.0.1"
assert port == 8000
assert transport == "http"
# Test transport override
monkeypatch.setenv("ARCADE_SERVER_TRANSPORT", "stdio")
host, port, transport = MCPApp._get_configuration_overrides("127.0.0.1", 8000, "http")
assert transport == "stdio"
monkeypatch.delenv("ARCADE_SERVER_TRANSPORT")
# Test host override (only works with HTTP transport)
monkeypatch.setenv("ARCADE_SERVER_TRANSPORT", "http")
monkeypatch.setenv("ARCADE_SERVER_HOST", "192.168.1.1")
host, port, transport = MCPApp._get_configuration_overrides("127.0.0.1", 8000, "http")
assert host == "192.168.1.1"
assert transport == "http"
monkeypatch.delenv("ARCADE_SERVER_HOST")
monkeypatch.delenv("ARCADE_SERVER_TRANSPORT")
# Test port override (only works with HTTP transport)
monkeypatch.setenv("ARCADE_SERVER_PORT", "9000")
host, port, transport = MCPApp._get_configuration_overrides("127.0.0.1", 8000, "http")
assert port == 9000
monkeypatch.delenv("ARCADE_SERVER_PORT")
# Test invalid port value
monkeypatch.setenv("ARCADE_SERVER_TRANSPORT", "http")
monkeypatch.setenv("ARCADE_SERVER_PORT", "invalid_port")
host, port, transport = MCPApp._get_configuration_overrides("127.0.0.1", 8000, "http")
assert port == 8000 # Should keep the default value
monkeypatch.delenv("ARCADE_SERVER_PORT")
monkeypatch.delenv("ARCADE_SERVER_TRANSPORT")
# Test host/port with stdio transport
monkeypatch.setenv("ARCADE_SERVER_TRANSPORT", "stdio")
monkeypatch.setenv("ARCADE_SERVER_HOST", "192.168.1.1")
monkeypatch.setenv("ARCADE_SERVER_PORT", "9000")
host, port, transport = MCPApp._get_configuration_overrides("127.0.0.1", 8000, "http")
# For stdio, host and port are still returned but not used by the server
assert host == "127.0.0.1" # Host should remain unchanged for stdio transport
assert port == 8000 # Port should remain unchanged for stdio transport
assert transport == "stdio"
monkeypatch.delenv("ARCADE_SERVER_HOST")
monkeypatch.delenv("ARCADE_SERVER_PORT")
monkeypatch.delenv("ARCADE_SERVER_TRANSPORT")