[Bug Fix] MCP: allow rubyui.com host in DNS-rebinding protection
Fixes #481
Root cause
The remote MCP endpoint at https://rubyui.com/mcp (and www.rubyui.com/mcp) rejected every JSON-RPC request with:
{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Forbidden: Invalid Host header"}}
RubyUI::MCP::RackApp#initialize (mcp/lib/ruby_ui/mcp/rack_app.rb) builds MCP::Server::Transports::StreamableHTTPTransport (from the third-party mcp gem) without an allowed_hosts: argument. That transport has built-in DNS-rebinding protection: it validates the incoming Host header against an allow list that defaults to loopback only (127.0.0.1, ::1, localhost). Since rubyui.com was never on that list, every real request got rejected by validate_host.
This is unrelated to Rails' own HostAuthorization middleware / config.hosts — that's commented out in docs/config/environments/production.rb and isn't the cause; the rejection happens entirely inside the mcp gem's transport.
Fix
RubyUI::MCP::RackApp now passes allowed_hosts: ["rubyui.com", "www.rubyui.com"] (plus the transport's own loopback defaults) when constructing the transport, extendable via a comma-separated RUBY_UI_MCP_ALLOWED_HOSTS env var — same pattern already used for RUBY_UI_MCP_REGISTRY in mcp/lib/ruby_ui/mcp/registry.rb.
allowed_origins: was not touched: the transport treats a request as same-origin (and thus allowed) whenever Origin matches Host, and non-browser MCP clients typically send no Origin header at all, so once Host is allow-listed the existing same-origin check already covers browser-based clients hitting rubyui.com/www.rubyui.com.
Second sub-problem (redirect)
The issue also reports https://rubyui.com/mcp 301-redirecting to https://www.rubyui.com/mcp, tripping up some MCP clients mid-handshake. I searched docs/config, docs/app/controllers, docs/config/routes.rb, and docs/Procfile and found no www-redirect logic, middleware, or infra config files (no _redirects, render.yaml, Cloudflare config, etc.) anywhere in the repo. config.force_ssl = true forces HTTPS only, not a www redirect. This redirect is happening at the Cloudflare/Render layer in front of the app, outside this repo — flagging as a known limitation/follow-up for whoever manages that infra config. This PR's fix does make it less harmful in the meantime: both rubyui.com and www.rubyui.com are now allow-listed, so a client that does follow the redirect will succeed on the second hop instead of failing on Host validation either way.
Test plan
- Added
mcp/test/rack_app_test.rb: asserts requests withHost: rubyui.com,Host: www.rubyui.com, andHost: localhostare not rejected with "Invalid Host header", assertsHost: evil.example.comis still rejected (DNS-rebinding protection intact), and covers theRUBY_UI_MCP_ALLOWED_HOSTSenv override. cd mcp && bundle exec rake test— 30 runs, 73 assertions, 0 failures, 0 errors, 0 skips.cd mcp && bundle exec standardrb lib/ruby_ui/mcp/rack_app.rb test/rack_app_test.rb— clean, no offenses. (Note:bundle exec rake standardacross the wholemcp/tree currently reports pre-existing offenses in unrelated files —mcp/has no committedGemfile.lock, sobundle installresolved newerstandard/rubocopversions than whatever last verified the tree; unrelated to this change.)docs/was not touched:/mcprouting itself doesn't change, so noSiteFiles/site_files:generateupdate is needed.docsdepends onruby_ui-mcpviapath: "../mcp", so this fix ships with the next docs deploy without any version bump or gem republish.
Summary by cubic
Fixes “Invalid Host header” errors on the MCP endpoint by allow-listing production hosts while keeping DNS-rebinding protection intact. Requests to https://rubyui.com/mcp and https://www.rubyui.com/mcp now work.
- Bug Fixes
- Pass
allowed_hoststoStreamableHTTPTransportwith["rubyui.com", "www.rubyui.com"], extendable viaRUBY_UI_MCP_ALLOWED_HOSTS. - Preserve loopback defaults; non-allowed hosts still return 403.
- Add tests covering production hosts, loopback, rejection of other hosts, and the env var override.
- Pass