go-sdk/mcp
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Paul Carleton 67bd3f2e2b
mcp: add automatic DNS rebinding protection for localhost servers (#760)
## Summary

Add DNS rebinding protection that is automatically enabled when requests
arrive via localhost (127.0.0.1, [::1]). This protects against malicious
websites using DNS rebinding to interact with local MCP servers.

(note: this was a claude assisted PR, mostly wanted to see how difficult
passing this test would be for this SDK)

## Design Goal: Secure by Default

The primary goal is to make it difficult to run a localhost server
without these protections by mistake. There are other approaches that
could provide secure defaults (e.g., a helper for `ListenAndServe` that
does the localhost check), but using `http.LocalAddrContextKey` for
runtime detection seemed like the most backwards compatible approach and
least likely to be disabled by accident.

With this implementation:
- **No code changes required** - existing servers get protection
automatically
- **No opt-in needed** - protection activates based on the connection's
local address
- **Explicit opt-out** - users must deliberately set
`DisableLocalhostProtection: true`

## Changes

- Add `DisableLocalhostProtection` option to `StreamableHTTPOptions`
- Add `isLocalhostAddr` and `isLocalhostHost` helper functions  
- Validate Host header at start of `ServeHTTP`, rejecting non-localhost
Host headers with 403 Forbidden

## How it works

The protection uses `http.LocalAddrContextKey` to detect the
connection's local address at runtime. When a request arrives via
localhost (127.0.0.1 or [::1]), the handler validates that the Host
header also matches a localhost value. If not, the request is rejected
with 403 Forbidden.

This approach means:
- Protection is enabled for requests arriving via localhost, regardless
of whether the server listens on `127.0.0.1` or `0.0.0.0`
- Requests arriving via non-localhost IPs (e.g., external network
requests) are **not** affected

### Edge case: Reverse proxies

If a reverse proxy (e.g., Envoy, nginx) runs on the same host and
forwards requests to the MCP server via localhost while preserving the
original Host header, those requests would be rejected. In this case,
users should either:
1. Set `DisableLocalhostProtection: true`
2. Configure the proxy to rewrite the Host header to localhost

## Testing

- Added unit tests for `isLocalhostAddr` and `isLocalhostHost` helper
functions
- Added integration tests for the full protection flow
- Verified against the MCP conformance test suite:
  - `localhost-host-rebinding-rejected`: PASS
  - `localhost-host-valid-accepted`: PASS

## Related

- Spec:
https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise
- TypeScript SDK implementation: `localhostHostValidation()` middleware
- Conformance test: `dns-rebinding-protection` scenario

---------

Co-authored-by: Maciek Kisiel <mkisiel@google.com>
2026-02-13 16:23:32 +01:00
..
testdata go.mod: upgrade to jsonschema v0.4.2 (#732) 2026-01-04 09:18:01 -05:00
capabilities_go125_test.go mcp: add Capabilities fields to ServerOptions and ClientOptions (#713) 2025-12-11 20:17:46 -05:00
client.go mcp: validation only for accept action (#766) 2026-01-28 17:06:06 +01:00
client_example_test.go mcp: fix goroutine leaks in unit tests (#496) 2025-10-16 11:13:30 -04:00
client_list_test.go mcp: allow any value for schemas (#517) 2025-09-26 11:55:54 -04:00
client_test.go feat: deprecated logger in client & add Logger in ClientOption (#738) 2026-01-05 07:41:31 -08:00
cmd.go mcp: speed up TestCommandTransportDuration 2025-09-12 12:00:29 -04:00
cmd_export_test.go mcp: speed up TestCommandTransportDuration 2025-09-12 12:00:29 -04:00
cmd_go125_test.go mcp: use synctest for timing-dependent tests (#756) 2026-02-10 10:40:11 +01:00
cmd_test.go mcp: use synctest for timing-dependent tests (#756) 2026-02-10 10:40:11 +01:00
conformance_go124_test.go internal/mcp: update protocol versions 2025-06-18 08:11:16 -07:00
conformance_go125_test.go internal/mcp: update protocol versions 2025-06-18 08:11:16 -07:00
conformance_test.go mcp: switch icon theme to named string type (#733) 2025-12-19 16:44:10 -05:00
content.go mcp: use omitzero json tag for byte array field in ResourceContents, instead of omitempty 2026-02-09 10:41:27 +01:00
content_nil_test.go mcp: add CallToolResult.getError (#481) 2025-09-18 08:08:23 -04:00
content_test.go mcp: switch icon theme to named string type (#733) 2025-12-19 16:44:10 -05:00
elicitation_go125_test.go mcp: use synctest for timing-dependent tests (#756) 2026-02-10 10:40:11 +01:00
elicitation_test.go mcp: use synctest for timing-dependent tests (#756) 2026-02-10 10:40:11 +01:00
error_test.go mcp: add Capabilities fields to ServerOptions and ClientOptions (#713) 2025-12-11 20:17:46 -05:00
event.go mcp: improve http transports error handling and make transport work with any size message (#734) 2026-02-05 12:59:37 +01:00
event_test.go mcp: improve http transports error handling and make transport work with any size message (#734) 2026-02-05 12:59:37 +01:00
features.go mcp: minor cleanup of some TODOs (#332) 2025-08-20 12:55:19 -04:00
features_test.go mcp: change tool handler design (#325) 2025-08-20 19:36:34 -04:00
logging.go mcp: use rand.Text and slog.DiscardHandler over internal implementation (#773) 2026-02-03 14:23:59 +01:00
mcp.go mcp: fix inaccurate package doc (#534) 2025-09-26 14:16:10 -04:00
mcp_example_test.go docs: add feature and troubleshooting documentation (#463) 2025-09-16 11:13:40 -04:00
mcp_go125_test.go mcp: use synctest for timing-dependent tests (#756) 2026-02-10 10:40:11 +01:00
mcp_test.go mcp: use synctest for timing-dependent tests (#756) 2026-02-10 10:40:11 +01:00
prompt.go mcp: align PromptHandler args with other handlers (#348) 2025-08-22 10:44:05 -04:00
protocol.go fix: add JSON tags to ElicitationCapabilities fields 2026-02-03 16:27:48 +01:00
protocol_test.go mcp: add CallToolResult.getError (#481) 2025-09-18 08:08:23 -04:00
requests.go mcp: support url mode elicitation (#646) 2025-11-23 12:58:28 -05:00
resource.go mcp: finalize cleanup of Go 1.23, make Go version support explicit 2026-02-06 12:50:01 +01:00
resource_test.go mcp/Resource: Refactor URI Template Matching with uritemplate Library (#100) 2025-07-08 15:52:54 -04:00
schema_cache.go perf: add schema caching to avoid repeated reflection (#685) 2026-01-20 20:16:46 -05:00
schema_cache_test.go perf: add schema caching to avoid repeated reflection (#685) 2026-01-20 20:16:46 -05:00
server.go mcp: use rand.Text and slog.DiscardHandler over internal implementation (#773) 2026-02-03 14:23:59 +01:00
server_example_test.go doc: document logging (#497) 2025-09-19 11:58:58 -04:00
server_test.go perf: add schema caching to avoid repeated reflection (#685) 2026-01-20 20:16:46 -05:00
session.go mcp: support stateless streamable sessions (#277) 2025-08-11 16:43:46 -04:00
shared.go mcp: use args struct for CloseSSEStream (#718) 2025-12-11 15:13:38 -05:00
shared_test.go all: remove ServerRequest[T] for concrete T (#342) 2025-08-21 12:10:53 -04:00
sse.go mcp: use rand.Text and slog.DiscardHandler over internal implementation (#773) 2026-02-03 14:23:59 +01:00
sse_example_test.go SSE: Add support for SSE handler options (#508) 2025-09-19 13:01:57 -04:00
sse_test.go mcp: add Allow header to 405 responses per RFC 9110 §15.5.6 (#757) 2026-01-22 09:43:45 +01:00
streamable.go mcp: add automatic DNS rebinding protection for localhost servers (#760) 2026-02-13 16:23:32 +01:00
streamable_bench_test.go mcp: don't persist failed streamable sessions (#605) 2025-10-27 16:29:16 -04:00
streamable_client.go mcp: export session missing error (#771) 2026-02-03 15:46:49 +01:00
streamable_client_test.go fix: enforce retry limit when SSE stream makes no progress (#742) 2026-02-03 10:17:32 +01:00
streamable_example_test.go mcp: fix goroutine leaks in unit tests (#496) 2025-10-16 11:13:30 -04:00
streamable_server.go mcp: simplify stream state management in streamable transport 2025-12-10 13:28:25 -05:00
streamable_test.go mcp: add automatic DNS rebinding protection for localhost servers (#760) 2026-02-13 16:23:32 +01:00
tool.go mcp: validate tool names (#640) 2025-11-19 17:01:50 -05:00
tool_example_test.go mcp: allow any value for schemas (#517) 2025-09-26 11:55:54 -04:00
tool_test.go all: expose JSON-RPC error codes, and support URLElicitationRequired errors (#655) 2025-12-01 11:29:17 -05:00
transport.go mcp: export session missing error (#771) 2026-02-03 15:46:49 +01:00
transport_example_test.go mcp: fix broken client root capabilities (#698) 2025-12-11 10:16:56 -05:00
transport_test.go fix: handle Windows CRLF in MCP client(#664) (#665) 2025-12-01 12:16:39 -05:00
util.go mcp: use rand.Text and slog.DiscardHandler over internal implementation (#773) 2026-02-03 14:23:59 +01:00