Skip to content

fix(mcp): respect ssl_verify config for StreamableHTTP servers#13038

Closed
lmoncany wants to merge 1 commit into
NousResearch:mainfrom
lmoncany:fix/mcp-ssl-verify-config
Closed

fix(mcp): respect ssl_verify config for StreamableHTTP servers#13038
lmoncany wants to merge 1 commit into
NousResearch:mainfrom
lmoncany:fix/mcp-ssl-verify-config

Conversation

@lmoncany

Copy link
Copy Markdown
Contributor

Problem

When an MCP server config has ssl_verify: false set in config.yaml (needed for local dev environments with self-signed certs — ServBay, LocalWP, MAMP, etc.), the value was read from the config but never actually passed to the httpx client.

This caused [SSL: CERTIFICATE_VERIFY_FAILED] errors at Hermes startup, the MCP server silently failing all 3 connection attempts, and the tools never appearing — even though the config looked correct.

Fix

Read ssl_verify from the server config block and pass it as the verify kwarg in both HTTP code paths:

  • New API (mcp >= 1.24.0): httpx.AsyncClient(verify=ssl_verify)
  • Legacy API (mcp < 1.24.0): streamablehttp_client(..., verify=ssl_verify)

Default is True (no change in behavior for existing configs without the key).

Testing

Verified against a local ServBay WordPress install (dev.afocal.com) with a self-signed cert. Before the fix: SSL errors on every startup. After: both wordpress and elementor MCP servers connect and register their tools successfully.

Config example this enables

mcp_servers:
  wordpress:
    url: https://dev.example.com/wp-json/mcp/mcp-adapter-default-server
    headers:
      Authorization: Basic <token>
    ssl_verify: false   # was silently ignored before this fix

When an MCP server config has ssl_verify: false (e.g. local dev with
a self-signed cert), the setting was read from config.yaml but never
passed to the httpx client, causing CERTIFICATE_VERIFY_FAILED errors
and silent connection failures.

Fix: read ssl_verify from config and pass it as the 'verify' kwarg to
both code paths:
- New API (mcp >= 1.24.0): httpx.AsyncClient(verify=ssl_verify)
- Legacy API (mcp < 1.24.0): streamablehttp_client(..., verify=ssl_verify)

Fixes local dev setups using ServBay, LocalWP, MAMP, or any stack with
a self-signed TLS certificate.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/mcp MCP client and OAuth area/config Config system, migrations, profiles labels Apr 22, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks @lmoncany! Your fix was cherry-picked and merged via #14343 with your authorship preserved.

briandevans added a commit to briandevans/hermes-agent that referenced this pull request Apr 30, 2026
…gle_api (NousResearch#14688)

macOS ships /usr/bin/python3 = 3.9.  When a local-model-backed agent uses
the terminal tool to run `python3 scripts/setup.py --check`, it picks up
that system interpreter rather than the Hermes venv's 3.11+.  On Py3.9
expressions like `Path | None` in a function signature evaluate at
def-time and raise `TypeError: unsupported operand type(s) for |: 'type'
and 'NoneType'` BEFORE the script's try/except ModuleNotFoundError
fallback can run.  Agents interpret the crash as "auth broken" and loop
through setup.

`from __future__ import annotations` (PEP 563) converts every annotation
into a string so the PEP 604 unions never fire at import time.  Py3.10+
users are unaffected.

Files in this PR:
- hermes_constants.py — shared infra, imported transitively by the skill
  scripts; has `Path | None`, `str | None`, `dict | None`, and two
  module-level `bool | None` annotations.
- skills/productivity/google-workspace/scripts/google_api.py — imported
  by setup.py's `--check` path; carries `str | None` and `dict | None`
  on `_gws_binary`/`_run_gws`.

Note: the equivalent fix for `scripts/setup.py` already landed on main
via NousResearch#13038 ("ship google-workspace deps as [google] extra; make setup.py
3.9-parseable").  The test file pins all three files (hermes_constants,
setup, google_api) so the regression guard catches future drift on any
of them.

Tests (tests/skills/test_py39_compat_google_workspace.py):
- AST check that each file starts with `from __future__ import
  annotations` (substring scan would pass false on a docstring mention).
- Verifies each file still contains PEP 604 unions — if a future edit
  strips them all, the pinned list should be reconsidered.
- Smoke test that hermes_constants loads and the previously-crashing
  `get_optional_skills_dir` and `parse_reasoning_effort` return their
  expected values.
- ast.parse(source, feature_version=(3, 9)) on each file — catches any
  Py3.10+-only syntax beyond PEP 604 (e.g. match/case).  Replaces the
  prior `py_compile`-based check, which under the CI interpreter (3.11)
  happily compiled 3.10+-only syntax — Copilot's review on NousResearch#15158
  flagged this and suggested ast.parse with feature_version.

No runtime behavior change on Py3.10+.  On Py3.9 the difference is an
import-time TypeError becoming a clean module load.

Closes NousResearch#14688

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists tool/mcp MCP client and OAuth type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants