Class McpHttpPolicy
Both are required by the MCP specification's transport security rules, and both are read from
the environment at run time rather than baked into generated source — the same pattern the bearer
token (MCP_HTTP_TOKEN) and the TLS keystore already use.
Binding
The spec says a server running locally SHOULD bind only to loopback rather than every
interface. Jetty's default is every interface, so the default here is "127.0.0.1"
and exposing the server beyond the machine is a deliberate act: set "MCP_HTTP_HOST".
That matters because the other two protections are opt-in — a server generated with neither
MCP_HTTP_TOKEN nor MCP_HTTPS answers anyone who can reach the port.
Origins
The spec requires that a server MUST validate the Origin header and answer 403
when it is present and invalid, to defeat DNS rebinding. Rebinding works by making the attacker's
page look same-origin to the browser, so the same-origin policy never engages and the
browser sends the request without a preflight; checking Origin server-side is what closes
it.
An absent Origin is allowed: non-browser MCP clients do not send one, and the
requirement is about rejecting a header that is present and wrong. A malformed one, or the literal
null that browsers send from sandboxed and file: contexts, is rejected.
With "MCP_ALLOWED_ORIGINS" unset, loopback origins are allowed and nothing else — which pairs with the loopback default above. Setting it replaces that default rather than adding to it, so the variable always states the whole allowlist; include the loopback forms explicitly if a local browser client still needs them. The single value "*" disables the check, which forfeits the protection and should be a last resort. Copyright 2003-2026 ATB Consultancy Services Ltd (formerly Orinda Software Ltd, Dublin, Ireland)
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe one "MCP_ALLOWED_ORIGINS" value that turns the origin check off.static final StringSet this to serve an unauthenticated server off loopback anyway.static final StringEnvironment variable holding a comma-separated allowlist of origins.static final StringEnvironment variable naming the interface to bind, e.g.static final StringLoopback, so a generated server is not on the network until someone says so.static final StringLargest request body accepted on the MCP endpoint. -
Method Summary
Modifier and TypeMethodDescriptionstatic StringbindHost()The interface the HTTP transport should bind, defaulting to loopback.static StringexposureRefusalReason(String theBindHost, boolean theAuthenticationGenerated) Why this server must not start, or null if it may.static McpHttpPolicyBuild the policy from the process environment.booleanTrue when this policy has been told to answer every origin.static booleanisLoopbackBindHost(String theHost) Whether a bind address keeps the server on this machine.booleanisOriginAllowed(String theOrigin) Whether a request carrying thisOriginmay proceed.static booleanWhether the exposure guard has been deliberately disabled — worth saying out loud.static longLargest request body to accept, or 0 for no cap.
-
Field Details
-
BIND_HOST_VARIABLE
Environment variable naming the interface to bind, e.g.0.0.0.0for all of them.- See Also:
-
ALLOWED_ORIGINS_VARIABLE
Environment variable holding a comma-separated allowlist of origins.- See Also:
-
DEFAULT_BIND_HOST
Loopback, so a generated server is not on the network until someone says so.- See Also:
-
ALLOW_ANY_ORIGIN
The one "MCP_ALLOWED_ORIGINS" value that turns the origin check off.- See Also:
-
ALLOW_UNAUTHENTICATED_VARIABLE
Set this to serve an unauthenticated server off loopback anyway.- See Also:
-
MAX_REQUEST_BYTES_VARIABLE
Largest request body accepted on the MCP endpoint. Unset means no cap.- See Also:
-
-
Method Details
-
fromEnvironment
Build the policy from the process environment. -
bindHost
The interface the HTTP transport should bind, defaulting to loopback.- Returns:
- a host suitable for
InetSocketAddress/ServerConnector.setHost
-
isAnyOriginAllowed
public boolean isAnyOriginAllowed()True when this policy has been told to answer every origin. -
isLoopbackBindHost
Whether a bind address keeps the server on this machine. -
exposureRefusalReason
Why this server must not start, or null if it may.Binding off loopback is the single act that puts a generated server on a network, and until now nothing tied it to the two features that protect one. Both of those are opt-in and both fail closed, so a config can quite reasonably ship without them — which is fine on loopback and not fine once the port is reachable. Publishing the container's MCP port needs
MCP_HTTP_HOST, so the exposing step is exactly where the question belongs.Authentication, specifically — TLS does not count. TLS encrypts the wire and restricts nobody; a server with TLS and no token is an open server that is merely hard to eavesdrop on. The thing that decides who may call is the bearer token.
- Parameters:
theBindHost- the interface about to be boundtheAuthenticationGenerated- whether this server was generated with bearer-token auth
-
isUnauthenticatedExposureOverridden
public static boolean isUnauthenticatedExposureOverridden()Whether the exposure guard has been deliberately disabled — worth saying out loud. -
maxRequestBytes
public static long maxRequestBytes()Largest request body to accept, or 0 for no cap.Opt-in rather than defaulted. A cap low enough to be useful against heap exhaustion is also low enough to reject a legitimate large argument — a base64 BLOB being written through a tool, say — and only the deployment knows which of its tools carry bulk. Defaulting this on would trade a rare failure for a routine one.
-
isOriginAllowed
Whether a request carrying thisOriginmay proceed.- Parameters:
theOrigin- the raw header value; null or blank when the client sent none- Returns:
- false only when the header is present and not allowed, which the caller answers 403
-