Only serve worker endpoints if secret is set (#691)

Default to `ARCADE_WORKER_SECRET` being unset. This env var must be
explicitly set now. Once it is set, the `worker/` endpoints will be
served.
This commit is contained in:
Eric Gustin 2025-11-24 14:39:14 -08:00 committed by GitHub
parent a921b76ce9
commit 44660d18ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 38 additions and 18 deletions

View file

@ -383,6 +383,7 @@ def start_server_process(entrypoint: str, debug: bool = False) -> tuple[subproce
"ARCADE_SERVER_PORT": str(port),
"ARCADE_SERVER_TRANSPORT": "http",
"ARCADE_AUTH_DISABLED": "true",
"ARCADE_WORKER_SECRET": "temp-validation-secret",
}
cmd = [sys.executable, entrypoint]

View file

@ -142,8 +142,8 @@ class ArcadeSettings(BaseSettings):
description="Disable authentication",
)
server_secret: str | None = Field(
default="dev",
description="Server secret",
default=None,
description="Server secret for worker endpoints (required to enable worker routes)",
validation_alias="ARCADE_WORKER_SECRET",
)
environment: str = Field(

View file

@ -123,15 +123,14 @@ def create_arcade_mcp(
**kwargs: Any,
) -> FastAPI:
"""
Create a FastAPI app exposing Arcade Worker and MCP HTTP endpoints.
Create a FastAPI app exposing MCP HTTP endpoints
and Arcade Worker endpoints if a secret is provided.
MCP is always enabled in this integrated application.
"""
if mcp_settings is None:
mcp_settings = MCPSettings.from_env()
secret = mcp_settings.arcade.server_secret
if secret is None:
secret = "dev" # noqa: S105
otel_handler = OTELHandler(
enable=otel_enable,
@ -180,13 +179,15 @@ def create_arcade_mcp(
app.add_middleware(AddTrailingSlashToPathMiddleware)
# Worker endpoints
worker = FastAPIWorker(
app=app,
secret=secret,
disable_auth=mcp_settings.arcade.auth_disabled,
otel_meter=otel_handler.get_meter(),
)
worker.catalog = catalog
if secret is not None:
worker = FastAPIWorker(
app=app,
secret=secret,
disable_auth=mcp_settings.arcade.auth_disabled,
otel_meter=otel_handler.get_meter(),
)
worker.catalog = catalog
logger.info("Worker routes enabled at /worker/* (ARCADE_WORKER_SECRET is set)")
class _MCPASGIProxy:
def __init__(self, parent_app: FastAPI):

View file

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "arcade-mcp-server"
version = "1.8.0"
version = "1.9.0"
description = "Model Context Protocol (MCP) server framework for Arcade.dev"
readme = "README.md"
authors = [{ name = "Arcade.dev" }]

View file

@ -64,6 +64,7 @@ def start_mcp_server(
"ARCADE_SERVER_PORT": str(port),
"ARCADE_SERVER_TRANSPORT": "http",
"ARCADE_AUTH_DISABLED": "true",
"ARCADE_WORKER_SECRET": "test-secret-e2e",
}
cmd = ["uv", "run", entrypoint_path, "http"]

View file

@ -44,12 +44,17 @@ def mock_router():
@pytest.fixture
def base_worker(mock_router):
# Save original value
original_secret = os.environ.get("ARCADE_WORKER_SECRET")
# Set env var temporarily for testing secret loading
os.environ["ARCADE_WORKER_SECRET"] = "test_secret_env" # noqa: S105
worker = BaseWorker()
worker.register_routes(mock_router) # Register routes using the mock router
# Clean up env var
del os.environ["ARCADE_WORKER_SECRET"]
# Restore original value
if original_secret is not None:
os.environ["ARCADE_WORKER_SECRET"] = original_secret
else:
del os.environ["ARCADE_WORKER_SECRET"]
return worker
@ -68,20 +73,32 @@ def test_base_worker_init_with_secret():
def test_base_worker_init_with_env_secret():
original_secret = os.environ.get("ARCADE_WORKER_SECRET")
os.environ["ARCADE_WORKER_SECRET"] = "env_secret_value" # noqa: S105
worker = BaseWorker()
assert worker.secret == "env_secret_value" # noqa: S105
assert not worker.disable_auth
del os.environ["ARCADE_WORKER_SECRET"]
# Restore secret to original if it was set
if original_secret is not None:
os.environ["ARCADE_WORKER_SECRET"] = original_secret
else:
del os.environ["ARCADE_WORKER_SECRET"]
def test_base_worker_init_no_secret_raises_error():
# Ensure env var is not set
# Ensure secret is not set
original_secret = os.environ.get("ARCADE_WORKER_SECRET")
if "ARCADE_WORKER_SECRET" in os.environ:
del os.environ["ARCADE_WORKER_SECRET"]
with pytest.raises(ValueError, match="No secret provided for worker"):
BaseWorker()
# Restore secret if it was set
if original_secret is not None:
os.environ["ARCADE_WORKER_SECRET"] = original_secret
def test_base_worker_init_disable_auth():
worker = BaseWorker(disable_auth=True)

View file

@ -1,6 +1,6 @@
[project]
name = "arcade-mcp"
version = "1.5.3"
version = "1.5.4"
description = "Arcade.dev - Tool Calling platform for Agents"
readme = "README.md"
license = {file = "LICENSE"}