Skip to content

HttpClientTransport sends Content-Type: application/json; charset=utf-8`, which is rejected by spec-strict MCP servers (e.g. GitHub Copilot returns 415) #1527

Closed

Description

@shmed

Describe the bug

The Streamable HTTP transport sends POST requests with Content-Type: application/json; charset=utf-8. Per IANA's registration for application/json and RFC 8259 §11, no charset parameter is defined for application/json — JSON is always Unicode and the parameter is meaningless.

GitHub Copilot's MCP endpoint enforces this strictly and rejects every request from the SDK with HTTP 415:

HTTP/1.1 415 Unsupported Media Type
Content-Type must be 'application/json'

This makes it impossible to connect to GitHub's MCP server (and any similarly strict server) without injecting a custom HttpClient that strips the charset parameter.

To Reproduce

Requires a GitHub Copilot–enabled PAT (any scope that can call https://api.githubcopilot.com/mcp — same token used in MCP Inspector).

// dotnet add package ModelContextProtocol --version 1.2.0
using ModelContextProtocol.Client;

var pat = Environment.GetEnvironmentVariable("GITHUB_COPILOT_PAT")
    ?? throw new InvalidOperationException("Set GITHUB_COPILOT_PAT");

var options = new HttpClientTransportOptions
{
    Endpoint = new Uri("https://api.githubcopilot.com/mcp"),
    TransportMode = HttpTransportMode.StreamableHttp,
    AdditionalHeaders = new Dictionary<string, string>
    {
        ["Authorization"] = $"Bearer {pat}",
    },
};

var transport = new HttpClientTransport(options);
await using var client = await McpClient.CreateAsync(transport);
// Throws: 415 Unsupported Media Type — Content-Type must be 'application/json'

The same PAT against the same endpoint works in MCP Inspector and with curl, confirming the issue is the SDK's Content-Type header, not the credential or the server.

curl showing the server accepts a bare application/json:

curl -i -X POST https://api.githubcopilot.com/mcp \
  -H "Authorization: Bearer $GITHUB_COPILOT_PAT" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"repro","version":"1.0"}}}'
# 200 OK with an SSE response

# Same request, but with charset=utf-8:
curl -i -X POST https://api.githubcopilot.com/mcp \
  -H "Authorization: Bearer $GITHUB_COPILOT_PAT" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"repro","version":"1.0"}}}'
# HTTP/1.1 415 Unsupported Media Type
# Content-Type must be 'application/json'

Expected behavior

The SDK should send Content-Type: application/json (no charset parameter) for JSON-RPC POST bodies, matching the IANA registration for application/json.

Actual behavior

System.Net.Http.HttpRequestException: Response status code does not indicate success: 415 (Unsupported Media Type). Response body: Content-Type must be 'application/json'
   at ModelContextProtocol.HttpResponseMessageExtensions.EnsureSuccessStatusCodeWithResponseBodyAsync(...)
   at ModelContextProtocol.Client.StreamableHttpClientSessionTransport.SendMessageAsync(...)
   at ModelContextProtocol.McpSessionHandler.SendRequestAsync(...)
   ...

Workaround

Inject a custom HttpClient (via the HttpClientTransport(options, httpClient, loggerFactory, ownsHttpClient) constructor) wrapped with a DelegatingHandler that strips the charset parameter before each request:

internal sealed class StrictJsonContentTypeHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
    {
        var contentType = request.Content?.Headers?.ContentType;
        if (contentType is not null &&
            string.Equals(contentType.MediaType, "application/json", StringComparison.OrdinalIgnoreCase) &&
            !string.IsNullOrEmpty(contentType.CharSet))
        {
            contentType.CharSet = null;
        }
        return base.SendAsync(request, ct);
    }
}

This works but every consumer hitting a strict server has to discover and re-implement it.

Environment

  • ModelContextProtocol 1.2.0
  • .NET 8.0
  • Windows 11 (also reproduces on Linux)

Additional context

This likely originates from System.Net.Http.Json / JsonContent.Create, which defaults to application/json; charset=utf-8. The fix is either to set Content.Headers.ContentType.CharSet = null after constructing the JsonContent, or to construct the StringContent/ByteArrayContent manually with the bare media type.

Activity

  1. jayaraman-venkatesan commented on Apr 21, 2026

    @jayaraman-venkatesan
    Contributor

    My triage -

    As mentioned the mcp client is adding charset here before making a call -

    private HttpContent? CreatePostBodyContent(JsonRpcMessage? message)

    The spec is silent on Content-Type for the client, it neither requires it nor defines what value it should have

    That said, other SDKs like some mentioned below are following this RFC by not including explicit charset :

    except Java sdk which adds charset - LINK

    This was a recent update - Issue

    I have opened a draft PR for this.

    If there are plans to address this, please let me know so I can convert my draft PR as open to review accordingly.

    @halter73 / @mikekistler / @jeffhandley

  2. mikekistler commented on Apr 22, 2026

    @mikekistler
    Contributor

    The linked IANA Registration for application/json says:

    Note: No "charset" parameter is defined for this registration.
    Adding one really has no effect on compliant recipients.

    Does this mean that "compliant recipients" ignore the "charset" parameter, and any recepient that errors because of the "charset" parameter is not "compliant"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions