You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(mcp): add Host/Origin validation for HTTP transports
Harden the MCP server's streamable-http and SSE transports by validating
the Host and Origin request headers against allowlists before any tool
call runs. The guard is on by default and auto-derives a safe allowlist
from the bind address, so loopback and local clients work with no config.
Non-loopback and reverse-proxy deployments can extend the allowlist (or
disable the guard) via a server.transport_security config block and
REDISVL_MCP_* environment variables, mirroring the existing auth pattern.
stdio has no network surface and is unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Streamable HTTP and SSE endpoints are **unauthenticated by default**. Binding to a non-loopback host without auth fails closed unless you pass `--allow-unauthenticated`; binding to loopback without auth only warns. For real deployments, enable JWT authentication (see {doc}`mcp_authentication`) rather than using `--allow-unauthenticated`. When not using `--read-only`, the `upsert-records` tool is also exposed to any client that can reach the server.
58
58
```
59
59
60
+
### Transport Security (Host / Origin Validation)
61
+
62
+
On the HTTP transports the server validates the request `Host` and `Origin` headers against allowlists before any tool runs. This is **on by default** and defends against [DNS rebinding](https://en.wikipedia.org/wiki/DNS_rebinding): without it, a malicious web page could rebind its own hostname to `127.0.0.1` and use the victim's browser to reach a local MCP server, even one bound to loopback.
63
+
64
+
-**Host:** the allowlist is derived automatically from the bind address. Loopback binds accept `localhost`, `127.0.0.1`, and `[::1]` (with or without the port), so local MCP clients work with no configuration.
65
+
-**Origin:** requests with no `Origin` header (typical of non-browser MCP clients) always pass. A request that carries a cross-site `Origin` is rejected unless that origin is explicitly allowlisted.
66
+
67
+
You only need to configure this for deployments where the client-visible `Host` differs from the bind address (a reverse proxy, or a `--host 0.0.0.0` bind reached via a public hostname). Use the `REDISVL_MCP_ALLOWED_HOSTS` / `REDISVL_MCP_ALLOWED_ORIGINS` env vars, or a `server.transport_security` block in the config:
68
+
69
+
```yaml
70
+
server:
71
+
redis_url: ${REDIS_URL}
72
+
transport_security:
73
+
allowed_hosts: [mcp.example.com] # extra Host values to accept
74
+
allowed_origins: [https://app.example.com] # browser origins to accept
# enabled: false # disable when a trusted proxy validates
77
+
```
78
+
79
+
```{note}
80
+
Behind a reverse proxy or gateway that already validates `Host`/`Origin`, set `server.transport_security.enabled: false` (or `allow_any_origin: true`) so the proxy's rewritten headers are not rejected.
81
+
```
82
+
60
83
Run it in read-only mode to expose search without upsert:
61
84
62
85
```bash
@@ -83,6 +106,10 @@ You can also control boot settings through environment variables:
83
106
|`REDISVL_MCP_READ_ONLY`| Disable `upsert-records` when set to `true`|
84
107
|`REDISVL_MCP_TOOL_SEARCH_DESCRIPTION`| Set the base search tool description text; RedisVL still appends schema-derived typed filter, `exists`, and `return_fields` hints |
85
108
|`REDISVL_MCP_TOOL_UPSERT_DESCRIPTION`| Override the upsert tool description |
109
+
|`REDISVL_MCP_ALLOWED_HOSTS`| Comma-separated extra `Host` header values to accept on HTTP transports (in addition to the bind-derived defaults) |
110
+
|`REDISVL_MCP_ALLOWED_ORIGINS`| Comma-separated browser `Origin` values to accept on HTTP transports |
111
+
|`REDISVL_MCP_ALLOW_ANY_ORIGIN`| Accept any `Origin` (for trusted-proxy setups) when set to `true`|
112
+
|`REDISVL_MCP_TRANSPORT_SECURITY_ENABLED`| Set to `false` to disable Host/Origin validation (e.g. behind a validating proxy) |
86
113
87
114
## Connect a Remote MCP Client
88
115
@@ -91,7 +118,7 @@ When using Streamable HTTP or SSE transport, point your MCP client at the server
91
118
-**Streamable HTTP**: `http://<host>:<port>/mcp`
92
119
-**SSE**: `http://<host>:<port>/sse`
93
120
94
-
> **Note:**`<host>` here is the bind address the server was started with. The default `127.0.0.1` only accepts connections from the same machine. To allow connections from other machines, start the server with `--host 0.0.0.0` and use the machine's actual IP or hostname in the client URL.
121
+
> **Note:**`<host>` here is the bind address the server was started with. The default `127.0.0.1` only accepts connections from the same machine. To allow connections from other machines, start the server with `--host 0.0.0.0` and use the machine's actual IP or hostname in the client URL. Because a `0.0.0.0` bind has no single canonical `Host`, add the client-visible hostname(s) to `REDISVL_MCP_ALLOWED_HOSTS` (or `server.transport_security.allowed_hosts`), or Host validation will reject those requests. See [Transport Security](#transport-security-host-origin-validation).
95
122
96
123
For example, to configure a remote MCP client to connect to a Streamable HTTP server running on `192.168.1.10:8000`:
0 commit comments