hermes - 💡(How to fix) Fix [Bug]: `mcp-protocol-version` header injected on `initialize` request, violating MCP spec

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Hermes Agent injects the mcp-protocol-version HTTP header on all MCP requests, including the initialize request. This violates the MCP spec, which only requires this header on subsequent requests (after initialization), and causes connection failures with compliant MCP servers that strictly validate the header.


Error Message

"error": { ...will fail to connect with Hermes out of the box, with no clear error message for the user. Start Hermes. Observe connection failure: Client error '400 Bad Request' "error": {

Additional Logs / Traceback (optional)

Root Cause

Root Cause Analysis (optional)

Fix Action

Workaround

Adding mcp-protocol-version explicitly to the server's headers config prevents Hermes from injecting its default:

graylog_mcp:
  url: http://example.com/api/mcp
  transport: streamable_http
  headers:
    Authorization: Basic <token>
    mcp-protocol-version: "2025-06-18"

Code Example

if not any(key.lower() == "mcp-protocol-version" for key in headers):
    headers["mcp-protocol-version"] = LATEST_PROTOCOL_VERSION

---

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid protocol version header 2025-11-25",
    "data": {"supported": ["2025-06-18"], "requested": "2025-11-25"}
  }
}

---

graylog_mcp:
  url: http://example.com/api/mcp
  transport: streamable_http
  headers:
    Authorization: Basic <token>
    mcp-protocol-version: "2025-06-18"

---

if not any(key.lower() == "mcp-protocol-version" for key in headers):
    if not is_initialization_request:
        headers["mcp-protocol-version"] = LATEST_PROTOCOL_VERSION

---

N/A

---
RAW_BUFFERClick to expand / collapse

Bug Description

Summary

Hermes Agent injects the mcp-protocol-version HTTP header on all MCP requests, including the initialize request. This violates the MCP spec, which only requires this header on subsequent requests (after initialization), and causes connection failures with compliant MCP servers that strictly validate the header.


Environment

  • Hermes Agent version: v2026.5.7
  • MCP SDK version: mcp 1.27.0 (Python)
  • Transport: Streamable HTTP
  • Affected file: tools/mcp_tool.py

Relevant Code

In tools/mcp_tool.py (lines 1284–1285):

if not any(key.lower() == "mcp-protocol-version" for key in headers):
    headers["mcp-protocol-version"] = LATEST_PROTOCOL_VERSION

This runs unconditionally for all requests, including the initialize request.


Expected Behaviour (per MCP spec)

Per the [MCP spec — Protocol Version Header](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#protocol-version-header):

"If using HTTP, the client MUST include the MCP-Protocol-Version HTTP header on all subsequent requests to the MCP server."

"Subsequent" explicitly refers to requests after initialization. The initialize request itself performs version negotiation via the protocolVersion field in the JSON-RPC body — the HTTP header is not required and should not be sent at this stage.


Actual Behaviour

Hermes sends mcp-protocol-version: 2025-11-25 on the initialize request. Any MCP server that strictly validates this header on all incoming requests (which is compliant behaviour per the spec's 400 requirement) will reject the connection if 2025-11-25 is not in its supported versions list.

Confirmed against Graylog 7.1.1, which returns:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid protocol version header 2025-11-25",
    "data": {"supported": ["2025-06-18"], "requested": "2025-11-25"}
  }
}

Note: the Python mcp SDK itself does not send this header on the initialize request — this behaviour is specific to Hermes.


Impact

Any MCP server that:

  1. Only supports an older protocol version (e.g. 2025-06-18), and
  2. Strictly validates the mcp-protocol-version header on all requests

...will fail to connect with Hermes out of the box, with no clear error message for the user.


Workaround

Adding mcp-protocol-version explicitly to the server's headers config prevents Hermes from injecting its default:

graylog_mcp:
  url: http://example.com/api/mcp
  transport: streamable_http
  headers:
    Authorization: Basic <token>
    mcp-protocol-version: "2025-06-18"

Suggested Fix

Skip injecting mcp-protocol-version when the outgoing message is the initialize request. Version negotiation for that request happens via params.protocolVersion in the JSON-RPC body, not via the HTTP header.

if not any(key.lower() == "mcp-protocol-version" for key in headers):
    if not is_initialization_request:
        headers["mcp-protocol-version"] = LATEST_PROTOCOL_VERSION

Steps to Reproduce

Option A — with any HTTP MCP server that supports only 2025-06-18 (e.g. Graylog 7.1.1):

Configure Hermes with an HTTP MCP server that does not support 2025-11-25:

yamlmcp_servers: test_server: url: http://<server>/mcp transport: streamable_http headers: Authorization: Basic <token>

Start Hermes. Observe connection failure: Client error '400 Bad Request' Check server logs — it will show rejection of mcp-protocol-version: 2025-11-25 on the initialize request

Option B — standalone, no external server needed: bash# Start a listener that prints raw HTTP requests python3 -c " import socket, threading

def handle(conn): data = conn.recv(4096).decode(errors='replace') print(data) conn.sendall(b'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 2\r\n\r\n{}') conn.close()

s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0', 9999)) s.listen(5) print('Listening on :9999') while True: conn, _ = s.accept() threading.Thread(target=handle, args=(conn,)).start() " Then configure Hermes: yamlmcp_servers: debug_server: url: http://localhost:9999 transport: streamable_http Start Hermes and observe the printed request — mcp-protocol-version: 2025-11-25 will be present on the initialize POST.

Expected Behavior

Expected Behaviour (per MCP spec) Per the MCP spec — Protocol Version Header:

"If using HTTP, the client MUST include the MCP-Protocol-Version HTTP header on all subsequent requests to the MCP server."

"Subsequent" explicitly refers to requests after initialization. The initialize request itself performs version negotiation via the protocolVersion field in the JSON-RPC body — the HTTP header is not required and should not be sent at this stage.

Actual Behavior

Hermes sends mcp-protocol-version: 2025-11-25 on the initialize request. Any MCP server that strictly validates this header on all incoming requests (which is compliant behaviour per the spec's 400 requirement) will reject the connection if 2025-11-25 is not in its supported versions list. Confirmed against Graylog 7.1.1, which returns: json{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid protocol version header 2025-11-25", "data": {"supported": ["2025-06-18"], "requested": "2025-11-25"} } } Note: the Python mcp SDK itself does not send this header on the initialize request — this behaviour is specific to Hermes.

Affected Component

Tools (terminal, file ops, web, code execution, etc.)

Messaging Platform (if gateway-related)

No response

Debug Report

N/A

Operating System

Ubuntu LTS 26

Python Version

No response

Hermes Version

v2026.5.7

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

In tools/mcp_tool.py (lines 1284–1285): pythonif not any(key.lower() == "mcp-protocol-version" for key in headers): headers["mcp-protocol-version"] = LATEST_PROTOCOL_VERSION This runs unconditionally for all requests, including the initialize request.

Proposed Fix (optional)

Skip injecting mcp-protocol-version when the outgoing message is the initialize request. Version negotiation for that request happens via params.protocolVersion in the JSON-RPC body, not via the HTTP header. pythonif not any(key.lower() == "mcp-protocol-version" for key in headers): if not is_initialization_request: headers["mcp-protocol-version"] = LATEST_PROTOCOL_VERSION

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: `mcp-protocol-version` header injected on `initialize` request, violating MCP spec