mcp: accept parameterized Content-Type types (#890)

Use a shared helper for Content-Type parsing in streamable transport
request validation and client response handling.

Follow up to https://github.com/modelcontextprotocol/go-sdk/pull/853,
where we did this for `Accept` headers.

This allows the streamable mcp server to validate the following header
`Content-Type: application/json;charset=utf-8`

Co-authored-by: Maciej Kisiel <mkisiel@google.com>
This commit is contained in:
Kalvin C 2026-04-23 05:51:47 -07:00 • committed by GitHub
commit 35f1b07e48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1506,9 +1506,9 @@ func (s streamableRequest) do(ctx context.Context, serverURL, sessionID string,
newSessionID := resp.Header.Get(sessionIDHeader)
contentType := resp.Header.Get("Content-Type")
contentType := baseMediaType(resp.Header.Get("Content-Type"))
var respBody []byte
if strings.HasPrefix(contentType, "text/event-stream") {
if contentType == "text/event-stream" {
r := readerInto{resp.Body, new(bytes.Buffer)}
for evt, err := range scanEvents(r) {
if err != nil {
@ -1525,7 +1525,7 @@ func (s streamableRequest) do(ctx context.Context, serverURL, sessionID string,
}
}
respBody = r.w.Bytes()
} else if strings.HasPrefix(contentType, "application/json") {
} else if contentType == "application/json" {
data, err := io.ReadAll(resp.Body)
if err != nil {
return newSessionID, resp.StatusCode, nil, fmt.Errorf("reading json body: %w", err)
@ -2047,6 +2047,28 @@ func TestStreamableGETWithoutEventStreamAccept(t *testing.T) {
}
}
func TestBaseMediaType(t *testing.T) {
tests := []struct {
name string
value string
want string
}{
{name: "empty", want: ""},
{name: "json", value: "application/json", want: "application/json"},
{name: "json with params", value: "Application/JSON; charset=utf-8", want: "application/json"},
{name: "event stream with params", value: "Text/Event-Stream; charset=utf-8", want: "text/event-stream"},
{name: "invalid", value: "application/json; charset", want: ""},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := baseMediaType(test.value); got != test.want {
t.Errorf("baseMediaType(%q) = %q, want %q", test.value, got, test.want)
}
})
}
}
func TestStreamableClientContextPropagation(t *testing.T) {
type contextKey string
const testKey = contextKey("test-key")