Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
schedule:
- cron: '31 9 * * 4'

# Declare default permissions as read only.
permissions: read-all

jobs:
analyze:
name: Analyze (${{ matrix.language }})
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "^1.25"
go-version: "^1.26"
- name: Start everything-server
run: |
go run ./conformance/everything-server/main.go -http=":3001" &
Expand All @@ -45,7 +45,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "^1.25"
go-version: "^1.26"
- name: "Run conformance tests"
uses: modelcontextprotocol/conformance@a2855b03582a6c0b31065ad4d9af248316ce61a3 # v0.1.15
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
steps:
- name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "^1.26"
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check docs are up-to-date
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "^1.25"
go-version: "^1.26"
- name: Set up Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: ["1.24", "1.25"]
go: ["1.25", "1.26"]
steps:
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -52,13 +52,15 @@ jobs:
run: go test -v ./...

race-test:
# Temporarily disable until fixes are prepared.
if: false
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.24"
go-version: "1.25"
- name: Test with -race
run: go test -v -race ./...
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ protocol.

See [troubleshooting.md](troubleshooting.md) for a troubleshooting guide.

# Backwards compatibility

See [mcpgodebug.md](mcpgodebug.md) for a list of backwards incompatible behavior changes
and description how they can be temporarily undone.

# Rough edges

See [rough_edges.md](rough_edges.md) for a list of rough edges or API
Expand Down
41 changes: 41 additions & 0 deletions docs/mcpgodebug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!-- Autogenerated by weave; DO NOT EDIT -->
# Backwards compatibility and MCPGODEBUG

According to our compatibility promise, we can't break backward compatibility
of the SDK API. However, sometimes we need to change the behavior of the SDK
in a backward-incompatible way in order to fix bugs or security issues.
In those cases we introduce temporary compatibility parameters, that can be
used to opt-out of the new behavior. They are usually maintained for two
minor release cycles and then removed.

The compatibility parameters are provided via the `MCPGODEBUG` environment
variable. The value of the variable is a comma-separated list of parameter
value assignments, e.g.:

```
MCPGODEBUG=parameter1=value1,parameter2=value2
```

## `MCPGODEBUG` history

### 1.4.1

Options listed below will be removed in the 1.6.0 version of the SDK.

- `disablecrossoriginprotection` added. If set to `1`, newly added cross-origin
protection will be disabled. The default behavior was changed to enable
cross-origin protection.

### 1.4.0

Options listed below will be removed in the 1.6.0 version of the SDK.

- `jsonescaping` added. If set to `1`, JSON marshaling will preserve the previous
behavior of escaping HTML characters in JSON strings. The default behavior
was changed to not escape HTML characters, to be consistent with other SDKs.

- `disablelocalhostprotection` added. If set to `1`, newly added DNS rebinding
protection will be disabled. The default behavior was changed to enable DNS rebinding
protection. The protection can also be disabled by setting the
`DisableLocalhostProtection` field in the `StreamableHTTPOptions` struct to
`true`, which is the recommended way to disable the protection long term.
2 changes: 1 addition & 1 deletion docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ and step-up authentication (when the server returns `insufficient_scope` error).
## Security

Here we discuss the mitigations described under
the MCP spec's [Security Best Practices](https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices) section, and how we handle them.
the MCP's [Security Best Practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices) section, and how we handle them.

### Confused Deputy

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/modelcontextprotocol/go-sdk

go 1.24.0
go 1.25.0

require (
github.com/golang-jwt/jwt/v5 v5.3.1
Expand Down
5 changes: 5 additions & 0 deletions internal/docs/README.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ protocol.

See [troubleshooting.md](troubleshooting.md) for a troubleshooting guide.

# Backwards compatibility

See [mcpgodebug.md](mcpgodebug.md) for a list of backwards incompatible behavior changes
and description how they can be temporarily undone.

# Rough edges

See [rough_edges.md](rough_edges.md) for a list of rough edges or API
Expand Down
1 change: 1 addition & 0 deletions internal/docs/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//go:generate weave -o ../../docs/server.md ./server.src.md
//go:generate weave -o ../../docs/troubleshooting.md ./troubleshooting.src.md
//go:generate weave -o ../../docs/rough_edges.md ./rough_edges.src.md
//go:generate weave -o ../../docs/mcpgodebug.md ./mcpgodebug.src.md

// The doc package generates the documentation at /doc, via go:generate.
//
Expand Down
40 changes: 40 additions & 0 deletions internal/docs/mcpgodebug.src.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Backwards compatibility and MCPGODEBUG

According to our compatibility promise, we can't break backward compatibility
of the SDK API. However, sometimes we need to change the behavior of the SDK
in a backward-incompatible way in order to fix bugs or security issues.
In those cases we introduce temporary compatibility parameters, that can be
used to opt-out of the new behavior. They are usually maintained for two
minor release cycles and then removed.

The compatibility parameters are provided via the `MCPGODEBUG` environment
variable. The value of the variable is a comma-separated list of parameter
value assignments, e.g.:

```
MCPGODEBUG=parameter1=value1,parameter2=value2
```

## `MCPGODEBUG` history

### 1.4.1

Options listed below will be removed in the 1.6.0 version of the SDK.

- `disablecrossoriginprotection` added. If set to `1`, newly added cross-origin
protection will be disabled. The default behavior was changed to enable
cross-origin protection.

### 1.4.0

Options listed below will be removed in the 1.6.0 version of the SDK.

- `jsonescaping` added. If set to `1`, JSON marshaling will preserve the previous
behavior of escaping HTML characters in JSON strings. The default behavior
was changed to not escape HTML characters, to be consistent with other SDKs.

- `disablelocalhostprotection` added. If set to `1`, newly added DNS rebinding
protection will be disabled. The default behavior was changed to enable DNS rebinding
protection. The protection can also be disabled by setting the
`DisableLocalhostProtection` field in the `StreamableHTTPOptions` struct to
`true`, which is the recommended way to disable the protection long term.
2 changes: 1 addition & 1 deletion internal/docs/protocol.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ and step-up authentication (when the server returns `insufficient_scope` error).
## Security

Here we discuss the mitigations described under
the MCP spec's [Security Best Practices](https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices) section, and how we handle them.
the MCP's [Security Best Practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices) section, and how we handle them.

### Confused Deputy

Expand Down
37 changes: 35 additions & 2 deletions mcp/streamable.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ type StreamableHTTPOptions struct {
// Only disable this if you understand the security implications.
// See: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise
DisableLocalhostProtection bool

// CrossOriginProtection allows to customize cross-origin protection.
// The deny handler set in the CrossOriginProtection through SetDenyHandler
// is ignored.
// If nil, default (zero-value) cross-origin protection will be used.
// Use `disablecrossoriginprotection` MCPGODEBUG compatibility parameter
// to disable the default protection until v1.6.0.
CrossOriginProtection *http.CrossOriginProtection
}

// NewStreamableHTTPHandler returns a new [StreamableHTTPHandler].
Expand All @@ -190,8 +198,10 @@ func NewStreamableHTTPHandler(getServer func(*http.Request) *Server, opts *Strea
h.opts = *opts
}

if h.opts.Logger == nil { // ensure we have a logger
h.opts.Logger = ensureLogger(nil)
h.opts.Logger = ensureLogger(h.opts.Logger)

if h.opts.CrossOriginProtection == nil {
h.opts.CrossOriginProtection = &http.CrossOriginProtection{}
}

return h
Expand Down Expand Up @@ -226,6 +236,13 @@ func (h *StreamableHTTPHandler) closeAll() {
// The option will be removed in the 1.6.0 version of the SDK.
var disablelocalhostprotection = mcpgodebug.Value("disablelocalhostprotection")

// disablecrossoriginprotection is a compatibility parameter that allows to disable
// the verification of the 'Origin' and 'Content-Type' headers, which was added in
// the 1.4.1 version of the SDK. See the documentation for the mcpgodebug package
// for instructions how to enable it.
// The option will be removed in the 1.6.0 version of the SDK.
var disablecrossoriginprotection = mcpgodebug.Value("disablecrossoriginprotection")

func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// DNS rebinding protection: auto-enabled for localhost servers.
// See: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise
Expand All @@ -238,6 +255,22 @@ func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Reque
}
}

if disablecrossoriginprotection != "1" {
// Verify the 'Origin' header to protect against CSRF attacks.
if err := h.opts.CrossOriginProtection.Check(req); err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
}
// Validate 'Content-Type' header.
if req.Method == http.MethodPost {
contentType := req.Header.Get("Content-Type")
if contentType != "application/json" {
http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType)
return
}
}
}

// Allow multiple 'Accept' headers.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept#syntax
accept := strings.Split(strings.Join(req.Header.Values("Accept"), ","), ",")
Expand Down
Loading
Loading