Skip to content

feat(mcp): add ALLOWED_MCP_DOMAINS env var for domain allowlist#3240

Merged
waleedlatif1 merged 3 commits intostagingfrom
feat/mcp-al
Feb 18, 2026
Merged

feat(mcp): add ALLOWED_MCP_DOMAINS env var for domain allowlist#3240
waleedlatif1 merged 3 commits intostagingfrom
feat/mcp-al

Conversation

@waleedlatif1
Copy link
Collaborator

Summary

  • Add ALLOWED_MCP_DOMAINS env var to restrict which external MCP server domains users can connect to
  • Server-side enforcement at all API surfaces: server registration (POST), update (PATCH), test connection, tool discovery, and tool execution
  • Platform's own hostname (from getBaseUrl()) is always implicitly allowed
  • Client-side validation in MCP settings UI with inline error feedback and disabled buttons
  • Gracefully handles both bare hostnames and full URLs in env var value
  • No-op when env var is unset — zero behavior change for existing deployments
  • Also includes minor SSO settings UI improvement: clipboard icon styling for callback URL copy button

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link

vercel bot commented Feb 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Feb 18, 2026 1:59am

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 18, 2026

Greptile Summary

Adds an ALLOWED_MCP_DOMAINS environment variable to restrict which external MCP server domains users can connect to. The implementation provides server-side enforcement at all MCP API surfaces (registration, update, test connection, tool discovery, and tool execution) and client-side validation in the MCP settings UI with inline error feedback. The platform's own hostname is always implicitly allowed. When the env var is unset, behavior is unchanged.

  • Server-side enforcement: Domain validation via validateMcpDomain() is added to POST (register), PATCH (update), and test-connection routes, returning 403 for blocked domains. The McpService layer filters blocked domains from getServerConfig and getWorkspaceServers, preventing tool discovery and execution against non-allowed servers.
  • Client-side validation: A new /api/settings/allowed-mcp-domains endpoint exposes the domain list to the UI, which disables submit/test buttons and shows inline errors when a URL's domain is not allowed.
  • Configuration: New env var added to env.ts, parsed in feature-flags.ts with normalization for both bare hostnames and full URLs. Helm chart updated with empty default.
  • Platform hostname caching bug: The getPlatformHostname() function in domain-check.ts uses a module-level boolean flag that permanently caches startup failures — if getBaseUrl() throws on first call, the platform hostname is locked to null for the process lifetime, potentially blocking the platform's own MCP connections.
  • Client/server validation inconsistency: The client-side isDomainAllowed returns true for unparseable URLs (allowing submission), while the server-side rejects them with 403, leading to confusing UX.
  • Also includes a minor SSO settings UI improvement: the callback URL copy button is moved inline with the label using a Clipboard icon.

Confidence Score: 3/5

  • Generally safe but has a caching bug that could silently block the platform's own MCP connections under certain startup conditions.
  • The overall architecture is well-designed with defense-in-depth (server-side enforcement at all API surfaces + service layer filtering + client-side UX). However, the module-level caching in getPlatformHostname() that permanently locks in startup failures is a real bug that could cause the platform to block its own hostname. The client/server inconsistency on malformed URL handling is a secondary UX issue. No tests were added for the new domain validation logic.
  • apps/sim/lib/mcp/domain-check.ts needs the caching bug fixed, and apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/mcp/mcp.tsx has a client/server validation inconsistency.

Important Files Changed

Filename Overview
apps/sim/lib/mcp/domain-check.ts New domain validation module with clean API, but the module-level caching of platform hostname permanently locks in startup failures, which could silently block the platform's own MCP server connections.
apps/sim/app/api/settings/allowed-mcp-domains/route.ts New unauthenticated API endpoint that exposes the domain allowlist configuration. Functional but lacks session validation unlike peer settings endpoints.
apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/mcp/mcp.tsx Client-side domain validation added with inline error feedback and disabled buttons. The isDomainAllowed function has inconsistent error handling with the server side — malformed URLs pass client validation but get rejected server-side.
apps/sim/lib/core/config/feature-flags.ts Clean addition of getAllowedMcpDomainsFromEnv() with proper domain normalization handling both bare hostnames and full URLs.
apps/sim/lib/core/config/env.ts Standard env var registration for ALLOWED_MCP_DOMAINS as optional string, consistent with existing patterns like BLACKLISTED_PROVIDERS.
apps/sim/lib/mcp/service.ts Domain filtering added at both single-server (getServerConfig) and multi-server (getWorkspaceServers) levels, ensuring blocked domains are silently excluded from tool discovery and execution.
apps/sim/app/api/mcp/servers/route.ts Domain validation added to POST handler for server registration. Correctly returns 403 for blocked domains and re-throws unexpected errors.
apps/sim/app/api/mcp/servers/[id]/route.ts Domain validation added to PATCH handler for server URL updates, consistent with the POST handler pattern.
apps/sim/app/api/mcp/servers/test-connection/route.ts Domain validation added to test-connection endpoint, preventing connection tests to blocked domains.
apps/sim/ee/sso/components/sso-settings.tsx Minor UI improvement: moved clipboard copy button inline with label, swapped Copy icon for Clipboard icon with smaller sizing. Purely cosmetic change.
helm/sim/values.yaml Added ALLOWED_MCP_DOMAINS env var to Helm chart with empty default (all domains allowed), consistent with existing env var patterns.

Flowchart

flowchart TD
    A[Client: MCP Settings UI] -->|fetch /api/settings/allowed-mcp-domains| B[GET allowed-mcp-domains route]
    B -->|reads env| C[getAllowedMcpDomainsFromEnv]
    C -->|returns domain list or null| B
    B -->|+ platform hostname| A

    A -->|user enters URL| D{isDomainAllowed?}
    D -->|No| E[Show inline error, disable buttons]
    D -->|Yes| F[Submit to API]

    F -->|POST /api/mcp/servers| G[POST route]
    F -->|PATCH /api/mcp/servers/:id| H[PATCH route]
    F -->|POST /api/mcp/servers/test-connection| I[Test Connection route]

    G --> J{validateMcpDomain}
    H --> J
    I --> J

    J -->|Domain blocked| K[403 McpDomainNotAllowedError]
    J -->|Domain allowed| L[Continue normal flow]

    M[McpService.getServerConfig] --> N{isMcpDomainAllowed}
    N -->|No| O[Return null / filter out]
    N -->|Yes| P[Return server config]

    Q[McpService.getWorkspaceServers] --> R{isMcpDomainAllowed filter}
    R -->|Blocked servers removed| S[Return filtered list]

    J --> T[checkMcpDomain]
    N --> T
    T --> U[getAllowedMcpDomainsFromEnv]
    T --> V[getPlatformHostname - cached]
Loading

Last reviewed commit: 9eda826

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

@waleedlatif1
Copy link
Collaborator Author

@cursor review

@waleedlatif1 waleedlatif1 merged commit 6421b1a into staging Feb 18, 2026
6 of 7 checks passed
@waleedlatif1 waleedlatif1 deleted the feat/mcp-al branch February 18, 2026 02:01
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

return createMcpErrorResponse(e, e.message, 403)
}
throw e
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing URL validation before domain check

Medium Severity

The POST endpoint validates domain before checking if URL exists. When an allowlist is configured and URL is missing, users get "Domain (empty) is not allowed" instead of "URL is required", creating confusing error messages. The test-connection endpoint correctly checks URL presence first (lines 67-73), but the POST endpoint doesn't follow this pattern.

Fix in Cursor Fix in Web

createdAt: server.createdAt.toISOString(),
updatedAt: server.updatedAt.toISOString(),
}))
.filter((config) => isMcpDomainAllowed(config.url))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cached tools bypass new domain filtering

Medium Severity

Tool discovery returns cached results without applying the new domain filtering. When ALLOWED_MCP_DOMAINS changes (requiring restart), persistent caches like Redis still contain tools from now-disallowed servers, which get returned directly. While tool execution would fail (due to getServerConfig filtering), the tools appear in discovery results, causing confusing UX where users see tools they can't use.

Fix in Cursor Fix in Web

}
throw e
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty URL bypasses domain validation in PATCH

Medium Severity

The PATCH endpoint uses a truthy check if (updateData.url) to guard domain validation, allowing empty strings to bypass validation. When an allowlist is configured and a client sends url: "" in the update, validation is skipped (empty string is falsy), and the database is updated with an unusable empty URL. The validateMcpDomain function correctly rejects empty strings when allowlist is configured, but the guard prevents it from being called. This creates broken server state.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments