ExMCP.Transport.SecurityGuard (ex_mcp v1.0.0-rc.8)

Copy Markdown View Source

Transport-layer security interceptor that enforces MCP security policies.

This module provides consistent security enforcement across all transports by intercepting outbound requests and applying token passthrough prevention and user consent validation.

The trust boundary

Every outbound URL is classified as :internal or :external by comparing its exact (scheme, host, effective port) origin against :trusted_origins. Deliberately broad host-only matching lives in :trusted_hosts. External requests

  1. have their credential headers removed (token passthrough prevention), and
  2. must be approved by the configured :consent_handler.

:trusted_origins defaults to empty, :trusted_hosts defaults to loopback, and :consent_handler defaults to ExMCP.ConsentHandler.Deny, so a remote MCP server is blocked until its exact origin is added to :trusted_origins:

config :ex_mcp, :security,
  trusted_origins: ["https://mcp.example.com"]

That single setting covers both checks — a trusted origin is never stripped and never prompts for consent. Consent then applies only to origins the application did not declare. See docs/SECURITY.md.

Both checks can be switched off individually with :enable_token_passthrough_prevention and :enable_user_consent_validation; prefer declaring :trusted_origins over disabling a control.

Summary

Functions

Gets the security configuration, merging provided config with defaults.

Validates a request against security policies.

Types

request()

@type request() :: %{
  url: String.t(),
  headers: [{String.t(), String.t()}],
  method: String.t(),
  transport: atom(),
  user_id: String.t()
}

security_result()

@type security_result() ::
  {:ok, sanitized_request :: map()} | {:error, security_violation :: map()}

Functions

get_security_config(config \\ %{})

@spec get_security_config(map()) :: map()

Gets the security configuration, merging provided config with defaults.

validate_request(request, config \\ %{})

@spec validate_request(request(), map()) :: security_result()

Validates a request against security policies.

This function enforces both token passthrough prevention and user consent validation for external resource access.

Parameters

  • request - Standardized request structure
  • config - Security configuration (optional, uses defaults if not provided)

Returns

  • {:ok, sanitized_request} - Request is allowed with potentially sanitized headers
  • {:error, security_violation} - Request blocked by security policy

Examples

request = %{
  url: "https://api.example.com/data",
  headers: [{"Authorization", "Bearer token"}],
  method: "GET",
  transport: :http,
  user_id: "user123"
}

case SecurityGuard.validate_request(request, config) do
  {:ok, sanitized_request} ->
    # Proceed with sanitized request
    perform_request(sanitized_request)

  {:error, violation} ->
    # Handle security violation
    {:error, violation}
end