pooled-http-session-content-type-non-idempotent
Pattern: Pooled HTTP sessions require explicit Content-Type on JSON bodies
When an MCP server uses a session-pooling HTTP client (requests.Session, httpx.Client, aiohttp.ClientSession, simple_salesforce’s connection object, etc.) to talk to upstream APIs, never rely on the requests library’s auto-detection of Content-Type from the json= kwarg. The auto-detection is non-idempotent under session reuse — the header gets set on first call but subsequent calls in the same session may inherit stale headers (or none) from prior calls.
Symptom
Upstream API returns intermittent INVALID_TYPE: Content-Type header must be {1} (Salesforce Tooling API), or 415 Unsupported Media Type, or silent body-parse failures, on a fraction of calls — usually the second or later call in a session.
Discovered in
salesforce-mcp v7.0.1 (2026-04-29). sf_run_apex_tests calling conn.restful("tooling/runTestsAsynchronous", method="POST", json=body) failed intermittently. sf_run_relevant_tests — which already passed headers={"Content-Type": "application/json"} explicitly — never failed.
Fix
Always pass an explicit Content-Type header on every JSON-body POST/PATCH:
JSON_HEADERS = {"Content-Type": "application/json"}
result = conn.restful(
"endpoint",
method="POST",
headers=JSON_HEADERS, # <-- mandatory, not optional
json=body,
)Centralize in core/tooling_helper.py (or equivalent) so every call site gets the same constant. Banning json= without headers= via a lint/test rule is the long-term safety net.
Generalization
Applies to ANY MCP server using a session-pooling HTTP client:
- simple_salesforce (Salesforce)
- Any custom requests.Session usage
- httpx.Client / AsyncClient with reuse
- aiohttp.ClientSession with reuse
Does NOT apply when each call creates a fresh client (rare in MCP servers — wasteful).
Related Salesforce-specific gotchas
- MetadataContainer.Name has a 32-char hard limit — variable-length name templates overflow for long identifiers
- ApexPage/ApexComponent meta XML requires
<label>, NOT<status>(the legacy one-size template was invalid for half the metadata catalog)
These three together constitute the salesforce-mcp v7.0.1 fix bundle.