Feb 27, 2026·5 min read·10 visits
The MCP Go SDK used Go's standard `encoding/json`, which happily accepts `Method` instead of `method`. Security tools (WAFs) often expect strict JSON-RPC compliance and only block `method`. This mismatch allows attackers to bypass filters by simply capitalizing JSON keys.
A high-severity interpretation conflict in the Model Context Protocol (MCP) Go SDK allows attackers to bypass security intermediaries. By exploiting Go's standard library JSON parsing behavior, which is case-insensitive by default, attackers can smuggle malicious payloads past WAFs that strictly adhere to the case-sensitive JSON-RPC 2.0 specification.
In the world of secure coding, "robustness" is often a double-edged sword. We are taught Postel's Law: "be conservative in what you do, be liberal in what you accept from others." But in security, being liberal is just another way of saying "please exploit me."
The Model Context Protocol (MCP) is the new connective tissue for LLMs, allowing AI models to talk to local tools and servers. The Go SDK (modelcontextprotocol/go-sdk) is the standard implementation for Go developers building these tools. You'd expect it to follow the JSON-RPC 2.0 spec to the letter. The spec is clear: field names are case-sensitive.
However, the Go SDK developers made a fatal, yet common, mistake: they trusted the standard library. Go's encoding/json is notoriously friendly. It tries very hard to map JSON keys to struct fields, ignoring case differences if an exact match isn't found. While this is great for developer velocity, it creates a massive "Interpretation Conflict" (CWE-436) when placed behind a security intermediary that actually follows the rules.
The vulnerability lies in how different systems interpret the same data stream. This is a classic semantic gap. On one side, you have a WAF or a sidecar proxy. These security devices are typically configured to be fast and pedantic. If the rule says block method: "exec", they look for those exact bytes. They assume that Method: "exec" is invalid JSON-RPC and ignore it, or let it pass because it doesn't match the blocklist.
On the other side, you have the vulnerable MCP Go SDK. It defines its request structures with standard Go tags:
type Request struct {
Method string `json:"method"`
// ... other fields
}When json.Unmarshal sees a JSON payload like {"Method": "..."}, it looks for a struct field named Method. It finds it. It checks the tag json:"method". It notices the case doesn't match, but because Go is "helpful," it says, "Eh, close enough," and populates the field anyway.
This behavior violates the JSON-RPC 2.0 specification, which explicitly states that member names are case-sensitive. The SDK was accepting malformed protocol messages that were semantically valid to the Go runtime but syntactically invalid to the protocol spec.
The fix required a fundamental shift in how the SDK handles JSON. You can't just tell the standard encoding/json to stop being case-insensitive; it's hardcoded behavior. The maintainers had to rip out the standard library and replace it with segmentio/encoding/json, a third-party library that offers more granular control.
Here is the critical change in internal/json/json.go. Notice the explicit disable switch:
// BEFORE: Using standard library (implicit in other files)
// err := json.Unmarshal(data, &v)
// AFTER: Using segmentio with case-sensitivity enforced
func Unmarshal(data []byte, v any) error {
dec := json.NewDecoder(bytes.NewReader(data))
// This line kills the vulnerability
dec.DontMatchCaseInsensitiveStructFields()
return dec.Decode(v)
}The patch (Commit 7b8d81c264074404abdf5aa16e2cf0c2d9c64cc0) introduces this wrapper and replaces all calls to json.Unmarshal throughout the codebase (mcp/client.go, mcp/server.go, etc.). It forces the parser to be pedantic: if the JSON key is Method but the struct tag expects method, the field is ignored (left zero-valued), or the parsing fails depending on validation logic.
Let's construct a real-world attack scenario. Imagine an MCP server exposing a sensitive tool, say filesystem/write. A security proxy sits in front of it, configured to allow filesystem/read but block filesystem/write.
The WAF rule might look something like this (pseudocode):
rule:
match:
json_key: "method"
value: "filesystem/write"
action: BLOCKThe Attack Chain:
Normal Request (Blocked):
The attacker sends valid JSON-RPC:
{"jsonrpc": "2.0", "method": "filesystem/write", "params": {...}}
The WAF sees method matches the blocklist. Request Dropped.
Bypass Request (Allowed):
The attacker capitalizes the key:
{"jsonrpc": "2.0", "Method": "filesystem/write", "params": {...}}
The WAF parses the JSON. It looks for the key method. It does not find it (it sees Method). It assumes this is a non-standard field or simply not the field it's watching. It forwards the request.
Execution:
The Go SDK receives the payload. json.Unmarshal matches Method (JSON) to Method (Struct). The handler for filesystem/write is invoked. The file is overwritten.
This is a textbook HTTP Request Smuggling attack, but adapted for the JSON-RPC application layer.
The mitigation is straightforward but requires a dependency change. If you are using modelcontextprotocol/go-sdk, you must upgrade to version 1.3.1 immediately. This version enforces strict case matching, aligning the Go implementation with the JSON-RPC 2.0 spec and other language implementations (like TypeScript).
If you cannot upgrade immediately, your defense-in-depth strategy needs a patch. You must reconfigure your WAFs and proxies to inspect all case variations of the method key. However, this is a losing battle. Can you guarantee your WAF will catch mEthod, METHOD, and Method? What about id vs ID?
This vulnerability serves as a reminder: when implementing strict protocols, do not rely on standard libraries that prioritize developer experience over specification compliance. Explicitly validate input, or use parsers that allow you to turn off "fuzzy" matching features.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
go-sdk modelcontextprotocol | < 1.3.1 | 1.3.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-436 (Interpretation Conflict) |
| Secondary CWE | CWE-178 (Improper Handling of Case Sensitivity) |
| CVSS v4.0 | 7.0 (High) |
| Attack Vector | Network (AV:N) |
| EPSS Score | 0.00048 (Low Probability) |
| Impact | Security Bypass (Subsequent System Integrity) |