hermes - 💡(How to fix) Fix Bad HTTP MCP endpoint returning HTML blocks discovery for 60s before CancelledError [2 pull requests]

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…

A misconfigured HTTP MCP server URL that responds immediately with HTML (for example pointing mcp_servers.<name>.url at a normal web app root instead of an MCP endpoint) blocks MCP discovery for the full default 60s connect timeout before surfacing CancelledError.

The SDK logs the real cause immediately:

ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8

…but Hermes does not fail that server fast. After ~60s it logs only:

WARNING tools.mcp_tool: Failed to connect to MCP server 'local_html_default_timeout': CancelledError

This makes each chat / one-shot startup feel hung whenever one enabled MCP URL is wrong.

Error Message

ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8 2026-05-31 12:43:19,858 ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8 Once the streamable HTTP client sees a non-MCP response/content type during initialization, Hermes should fail that server promptly and preserve the actionable error (Unexpected content type: text/html; charset=utf-8) instead of waiting for the default timeout and reporting CancelledError.

  • improve the final warning to include the underlying content-type error rather than replacing it with CancelledError.

Root Cause

A misconfigured HTTP MCP server URL that responds immediately with HTML (for example pointing mcp_servers.<name>.url at a normal web app root instead of an MCP endpoint) blocks MCP discovery for the full default 60s connect timeout before surfacing CancelledError.

The SDK logs the real cause immediately:

ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8

…but Hermes does not fail that server fast. After ~60s it logs only:

WARNING tools.mcp_tool: Failed to connect to MCP server 'local_html_default_timeout': CancelledError

This makes each chat / one-shot startup feel hung whenever one enabled MCP URL is wrong.

Fix Action

Fixed

Code Example

ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8

---

WARNING tools.mcp_tool: Failed to connect to MCP server 'local_html_default_timeout': CancelledError

---

_DEFAULT_CONNECT_TIMEOUT = 60
_MAX_INITIAL_CONNECT_RETRIES = 3
_MAX_BACKOFF_SECONDS = 60

---

import http.server
import logging
import socketserver
import sys
import threading
import time

sys.path.insert(0, "/home/zohar/.hermes/hermes-agent")
from tools import mcp_tool

logging.basicConfig(level=logging.WARNING, format="%(asctime)s %(levelname)s %(name)s: %(message)s")

class HtmlHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("content-type", "text/html; charset=utf-8")
        self.end_headers()
        self.wfile.write(b"<html>not mcp</html>")
    do_POST = do_GET
    def log_message(self, *_):
        return

with socketserver.TCPServer(("127.0.0.1", 0), HtmlHandler) as server:
    port = server.server_address[1]
    threading.Thread(target=server.serve_forever, daemon=True).start()
    t0 = time.monotonic()
    tools = mcp_tool.register_mcp_servers({
        "local_html_default_timeout": {"url": f"http://127.0.0.1:{port}"}
    })
    dt = time.monotonic() - t0
    print(f"local_html_default_timeout: seconds={dt:.2f} tools={tools}")
    mcp_tool.shutdown_mcp_servers()
    server.shutdown()

---

2026-05-31 12:43:19,858 ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8
2026-05-31 12:44:23,421 WARNING tools.mcp_tool: Failed to connect to MCP server 'local_html_default_timeout': CancelledError
local_html_default_timeout: seconds=60.06 tools=[]

---

local_html_connect_timeout_1: seconds=1.01 tools=[]
RAW_BUFFERClick to expand / collapse

Summary

A misconfigured HTTP MCP server URL that responds immediately with HTML (for example pointing mcp_servers.<name>.url at a normal web app root instead of an MCP endpoint) blocks MCP discovery for the full default 60s connect timeout before surfacing CancelledError.

The SDK logs the real cause immediately:

ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8

…but Hermes does not fail that server fast. After ~60s it logs only:

WARNING tools.mcp_tool: Failed to connect to MCP server 'local_html_default_timeout': CancelledError

This makes each chat / one-shot startup feel hung whenever one enabled MCP URL is wrong.

Environment

  • Hermes Agent v0.15.2 (v2026.5.29.2, source commit 77a1650c7)
  • Python 3.12.3
  • mcp 1.26.0
  • httpx 0.28.1
  • WSL Ubuntu

Static comparison against v0.14.0 (v2026.5.16) shows the relevant retry defaults are unchanged:

_DEFAULT_CONNECT_TIMEOUT = 60
_MAX_INITIAL_CONNECT_RETRIES = 3
_MAX_BACKOFF_SECONDS = 60

So this does not look like a new v0.15-only regression, but v0.15.2 is still affected.

Minimal reproducer

Run from the Hermes source tree / venv:

import http.server
import logging
import socketserver
import sys
import threading
import time

sys.path.insert(0, "/home/zohar/.hermes/hermes-agent")
from tools import mcp_tool

logging.basicConfig(level=logging.WARNING, format="%(asctime)s %(levelname)s %(name)s: %(message)s")

class HtmlHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("content-type", "text/html; charset=utf-8")
        self.end_headers()
        self.wfile.write(b"<html>not mcp</html>")
    do_POST = do_GET
    def log_message(self, *_):
        return

with socketserver.TCPServer(("127.0.0.1", 0), HtmlHandler) as server:
    port = server.server_address[1]
    threading.Thread(target=server.serve_forever, daemon=True).start()
    t0 = time.monotonic()
    tools = mcp_tool.register_mcp_servers({
        "local_html_default_timeout": {"url": f"http://127.0.0.1:{port}"}
    })
    dt = time.monotonic() - t0
    print(f"local_html_default_timeout: seconds={dt:.2f} tools={tools}")
    mcp_tool.shutdown_mcp_servers()
    server.shutdown()

Observed output:

2026-05-31 12:43:19,858 ERROR mcp.client.streamable_http: Unexpected content type: text/html; charset=utf-8
2026-05-31 12:44:23,421 WARNING tools.mcp_tool: Failed to connect to MCP server 'local_html_default_timeout': CancelledError
local_html_default_timeout: seconds=60.06 tools=[]

For comparison, adding connect_timeout: 1 to the same bad server makes the failure return in ~1s:

local_html_connect_timeout_1: seconds=1.01 tools=[]

Expected behavior

Once the streamable HTTP client sees a non-MCP response/content type during initialization, Hermes should fail that server promptly and preserve the actionable error (Unexpected content type: text/html; charset=utf-8) instead of waiting for the default timeout and reporting CancelledError.

Possible fixes:

  • wrap streamable_http_client / session.initialize() in a shorter initialization timeout separate from the general 60s connect timeout;
  • treat the SDK's immediate non-MCP content-type failure as fatal and cancel the transport task promptly;
  • improve the final warning to include the underlying content-type error rather than replacing it with CancelledError.

enabled: false is honored in v0.15.2, so disabled MCP entries are not the issue once config is cleaned up. The blocking comes from any enabled bad HTTP MCP URL.

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…

FAQ

Expected behavior

Once the streamable HTTP client sees a non-MCP response/content type during initialization, Hermes should fail that server promptly and preserve the actionable error (Unexpected content type: text/html; charset=utf-8) instead of waiting for the default timeout and reporting CancelledError.

Possible fixes:

  • wrap streamable_http_client / session.initialize() in a shorter initialization timeout separate from the general 60s connect timeout;
  • treat the SDK's immediate non-MCP content-type failure as fatal and cancel the transport task promptly;
  • improve the final warning to include the underlying content-type error rather than replacing it with CancelledError.

enabled: false is honored in v0.15.2, so disabled MCP entries are not the issue once config is cleaned up. The blocking comes from any enabled bad HTTP MCP URL.

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 Bad HTTP MCP endpoint returning HTML blocks discovery for 60s before CancelledError [2 pull requests]