Skip to content
Draft
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
12 changes: 10 additions & 2 deletions backend/api_v2/execution_urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
from django.urls import re_path
from django.urls import include, re_path
from rest_framework.urlpatterns import format_suffix_patterns

from api_v2.api_deployment_views import DeploymentExecution

execute = DeploymentExecution.as_view()


urlpatterns = format_suffix_patterns(
# The deployment MCP server hangs off the execution URL
# (``…/<api_name>/mcp``), so it is matched first. The execution pattern below
# anchors with ``$`` and cannot swallow the ``/mcp`` suffix on its own, but
# ordering it first keeps that independent of the other pattern's exact shape.
# Kept outside ``format_suffix_patterns`` because that helper rewrites leaf
# patterns and is not meant to wrap an ``include()``.
urlpatterns = [re_path(r"^", include("mcp_server.urls"))]

urlpatterns += format_suffix_patterns(
[
re_path(
r"^api/(?P<org_name>[\w-]+)/(?P<api_name>[\w-]+)/?$",
Expand Down
17 changes: 16 additions & 1 deletion backend/backend/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ def get_required_setting(setting_key: str, default: str | None = None) -> str |
REDIS_HOST = os.environ.get("REDIS_HOST", "localhost")
REDIS_PORT = os.environ.get("REDIS_PORT", "6379")
REDIS_DB = os.environ.get("REDIS_DB", "")
# Redis DB for MCP server state (currently the billable-call budget). Defaults
# to REDIS_DB, so out of the box MCP state shares the general cache DB and
# nothing changes. It exists as a separate knob so MCP state can later be moved
# to its own DB — for isolation, or so a cache flush does not reset in-flight
# budget windows — without that becoming a breaking change.
MCP_REDIS_DB = int(os.environ.get("MCP_REDIS_DB") or REDIS_DB or 0)
SESSION_EXPIRATION_TIME_IN_SECOND = os.environ.get(
"SESSION_EXPIRATION_TIME_IN_SECOND", 3600
)
Expand All @@ -119,6 +125,12 @@ def get_required_setting(setting_key: str, default: str | None = None) -> str |
"API_DEPLOYMENT_PATH_PREFIX", "deployment"
).strip("/")

# Budget for billable MCP tool calls (LLM inference, indexing, pipeline runs),
# counted per organization over a rolling window. Bounds how often an agent can
# trigger paid work; it counts calls, not tokens — see mcp_server/spend_guard.py.
MCP_BILLABLE_CALL_LIMIT = int(os.environ.get("MCP_BILLABLE_CALL_LIMIT", 50))
MCP_BILLABLE_WINDOW_SECONDS = int(os.environ.get("MCP_BILLABLE_WINDOW_SECONDS", 3600))

# Maximum file size for presigned URLs in API deployments (in MB)
API_DEPL_PRESIGNED_URL_MAX_FILE_SIZE_MB = int(
os.environ.get("API_DEPL_PRESIGNED_URL_MAX_FILE_SIZE_MB", 20)
Expand Down Expand Up @@ -379,6 +391,7 @@ def filter(self, record):
"pipeline_v2",
"platform_settings_v2",
"api_v2",
"mcp_server",
"usage_v2",
"notification_v2",
"prompt_studio.prompt_profile_manager_v2",
Expand Down Expand Up @@ -647,7 +660,9 @@ def filter(self, record):
"/static",
]
WHITELISTED_PATHS = [f"/{PATH_PREFIX}{PATH}" for PATH in WHITELISTED_PATHS_LIST]
# White lists workflow-api-deployment path
# White lists workflow-api-deployment path. This also covers the deployment MCP
# server, which hangs off the same URL and authenticates with the deployment's
# own API key rather than a session.
WHITELISTED_PATHS.append(f"/{API_DEPLOYMENT_PATH_PREFIX}")

# Whitelisting health check API
Expand Down
4 changes: 4 additions & 0 deletions backend/backend/urls_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,8 @@
path("execution/", include("workflow_manager.file_execution.urls")),
path("metrics/", include("dashboard_metrics.urls")),
path("platform-api/", include("platform_api.urls")),
# Organization-scoped MCP server. Mounted on this tenant path rather than
# under the deployment prefix so CustomAuthMiddleware authenticates the
# platform API key — that prefix is whitelisted and would bypass auth.
path("mcp/", include("mcp_server.platform_urls")),
]
Loading