Bug: MCP client sends empty _meta:{} on every request → Meta Ads MCP server rejects with HTTP 400
Summary
Hermes cannot natively connect to Meta's official hosted Ads MCP server
(https://mcp.facebook.com/ads). Every JSON-RPC request the bundled MCP client sends
carries an empty _meta: {} object in params. Meta's server strictly rejects this
with HTTP 400 (-32602 "_meta for Request must be a dict or null"), so initialize
fails and the server is parked after 3 attempts.
Other MCP clients Meta officially documents (Claude Code, ChatGPT) do not emit an
empty _meta, so they connect fine — this is a client-side compatibility issue, not a
Meta outage.
Root cause
mcp/shared/jsonrpc_dispatcher.py (vendored MCP SDK), send_raw_request, ~lines 356–362:
out_params = dict(params) if params is not None else {}
out_meta = dict(out_params.get("_meta") or {}) # -> {}
on_progress = opts.get("on_progress")
if on_progress is not None:
out_meta["progressToken"] = request_id
out_params["_meta"] = out_meta # unconditionally attaches _meta, even when empty
out_params["_meta"] is set even when out_meta is {} (no progress token, no trace
context from the no-op tracer). The wire payload becomes:
{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-11-25","capabilities":{},
"clientInfo":{"name":"mcp","version":"0.1.0"},"_meta":{}}}
Reproduction (identical requests, only _meta differs)
# WITH _meta:{} -> HTTP 400
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://mcp.facebook.com/ads \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
--data-raw '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"mcp","version":"0.1.0"},"_meta":{}}}'
# WITHOUT _meta -> HTTP 200
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://mcp.facebook.com/ads \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
--data-raw '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"mcp","version":"0.1.0"}}}'
Confirmed on this machine: 400 vs 200 respectively. _meta:null is ALSO rejected 400 by
Meta, so passing null is not a workaround — the field must be omitted entirely.
Suggested fix
Only attach _meta when it has content:
out_params = dict(params) if params is not None else {}
out_meta = dict(out_params.get("_meta") or {})
on_progress = opts.get("on_progress")
if on_progress is not None:
out_meta["progressToken"] = request_id
# ... trace-context injection into out_meta ...
if out_meta: # <-- only set when non-empty
out_params["_meta"] = out_meta
elif "_meta" in out_params:
del out_params["_meta"]
Note inject_trace_context(out_meta) (line ~390) may add W3C trace keys; the emptiness
check must run AFTER that injection so a real trace context is still sent. When the tracer
is a no-op (default), out_meta stays {} and should be omitted.
Impact
Any strict MCP server that rejects an empty _meta object is unreachable natively from
Hermes. Meta's hosted Ads MCP server is the first confirmed case. Current workaround:
call the server over raw curl (which omits _meta) — works, but bypasses native MCP
integration and the settings UI.
Environment
- Hermes MCP client: bundled
mcp SDK under hermes-agent/venv/.../mcp/
- Server:
Meta Ads MCP Server v1.0.0, https://mcp.facebook.com/ads
- Protocol negotiated: server replies
2025-06-18
Bug: MCP client sends empty
_meta:{}on every request → Meta Ads MCP server rejects with HTTP 400Summary
Hermes cannot natively connect to Meta's official hosted Ads MCP server
(
https://mcp.facebook.com/ads). Every JSON-RPC request the bundled MCP client sendscarries an empty
_meta: {}object inparams. Meta's server strictly rejects thiswith HTTP 400 (
-32602 "_meta for Request must be a dict or null"), soinitializefails and the server is parked after 3 attempts.
Other MCP clients Meta officially documents (Claude Code, ChatGPT) do not emit an
empty
_meta, so they connect fine — this is a client-side compatibility issue, not aMeta outage.
Root cause
mcp/shared/jsonrpc_dispatcher.py(vendored MCP SDK),send_raw_request, ~lines 356–362:out_params["_meta"]is set even whenout_metais{}(no progress token, no tracecontext from the no-op tracer). The wire payload becomes:
{"jsonrpc":"2.0","id":1,"method":"initialize", "params":{"protocolVersion":"2025-11-25","capabilities":{}, "clientInfo":{"name":"mcp","version":"0.1.0"},"_meta":{}}}Reproduction (identical requests, only
_metadiffers)Confirmed on this machine: 400 vs 200 respectively.
_meta:nullis ALSO rejected 400 byMeta, so passing
nullis not a workaround — the field must be omitted entirely.Suggested fix
Only attach
_metawhen it has content:Note
inject_trace_context(out_meta)(line ~390) may add W3C trace keys; the emptinesscheck must run AFTER that injection so a real trace context is still sent. When the tracer
is a no-op (default),
out_metastays{}and should be omitted.Impact
Any strict MCP server that rejects an empty
_metaobject is unreachable natively fromHermes. Meta's hosted Ads MCP server is the first confirmed case. Current workaround:
call the server over raw curl (which omits
_meta) — works, but bypasses native MCPintegration and the settings UI.
Environment
mcpSDK underhermes-agent/venv/.../mcp/Meta Ads MCP Server v1.0.0,https://mcp.facebook.com/ads2025-06-18