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
- have their credential headers removed (token passthrough prevention), and
- 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
Functions
Gets the security configuration, merging provided config with defaults.
@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 structureconfig- 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