Skip to content

fix(catalog): wire api_key auth headers for http MCP servers - #70775

Open
JonthanaHanh wants to merge 1 commit into
NousResearch:mainfrom
JonthanaHanh:fix/mcp-catalog-http-api-key-headers
Open

fix(catalog): wire api_key auth headers for http MCP servers#70775
JonthanaHanh wants to merge 1 commit into
NousResearch:mainfrom
JonthanaHanh:fix/mcp-catalog-http-api-key-headers

Conversation

@JonthanaHanh

Copy link
Copy Markdown
Contributor

Problem

When an optional-mcps manifest declares transport.type: http with auth.type: api_key, install_entry() correctly prompts for the key and saves it to .env, but _build_server_config() only handled the oauth case. The api_key case produced a bare url entry with no headers, so every request to the server was unauthenticated (→ 401).

Root Cause

In hermes_cli/mcp_catalog.py, _build_server_config() (line 489-492):

elif t.type == http:
    cfg[url] = t.url
    if entry.auth.type == oauth:
        cfg[auth] = oauth
    # ← api_key case: nothing written, headers missing

Fix

Import and call _bearer_auth_headers(entry.name) from mcp_config.py to produce the same Authorization: Bearer ${MCP_<NAME>_API_KEY} template used by the manual hermes mcp add --url path.

elif t.type == http:
    cfg[url] = t.url
    if entry.auth.type == oauth:
        cfg[auth] = oauth
    elif entry.auth.type == api_key:
        from hermes_cli.mcp_config import _bearer_auth_headers
        cfg[headers] = _bearer_auth_headers(entry.name)

Tests

  • test_http_api_key_builds_bearer_headers_template: unit test for _build_server_config() verifying the template is correct
  • test_install_http_api_key_writes_bearer_headers: integration test verifying end-to-end install writes headers to config

All 41 tests in tests/hermes_cli/test_mcp_catalog.py pass.

Closes #70632

When an optional-mcps manifest declares transport.type=http with
auth.type=api_key, install_entry() correctly prompts for the key and
saves it to .env, but _build_server_config() only handled the oauth
case — the api_key case produced a bare url entry with no headers,
so every request to the server was unauthenticated (→ 401).

Import and call _bearer_auth_headers(entry.name) from mcp_config.py
to produce the same Authorization: Bearer ${MCP_<NAME>_API_KEY}
template used by the manual 'hermes mcp add --url' path.

Closes NousResearch#70632.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard tool/mcp MCP client and OAuth labels Jul 24, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused fix. The current HTTP catalog path does omit API-key headers (hermes_cli/mcp_catalog.py:505-508), but this implementation does not preserve the manifest credential contract.

Problems

  • hermes_cli/mcp_catalog.py:496 derives MCP_<NAME>_API_KEY from the entry name. install_entry() instead prompts and saves each declared auth.env variable (hermes_cli/mcp_catalog.py:733-736), and arbitrary names are valid (tests/hermes_cli/test_mcp_catalog.py:175-194 uses DEMO_KEY). An HTTP entry named demo using that valid manifest writes ${MCP_DEMO_API_KEY} while saving DEMO_KEY; unmatched placeholders remain literal at runtime (tools/mcp_tool.py:4544-4548).
  • The new tests only cover the canonical MCP_DEMO_API_KEY spelling, so they miss this mismatch.

Suggested changes

  • Select and validate the header variable from the manifest's declared credential field, then add an HTTP install test using a noncanonical declared name such as DEMO_KEY.

Automated hermes-sweeper review.

Comment thread hermes_cli/mcp_catalog.py
elif entry.auth.type == "api_key":
from hermes_cli.mcp_config import _bearer_auth_headers

cfg["headers"] = _bearer_auth_headers(entry.name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper always generates MCP_<entry-name>_API_KEY, but catalog auth.env accepts arbitrary names and install_entry() saves the declared name. A valid demo manifest declaring DEMO_KEY would save DEMO_KEY yet persist ${MCP_DEMO_API_KEY}, leaving the Authorization placeholder unresolved. Please derive the header variable from a declared manifest credential and cover that noncanonical case.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Four PRs address or reference Issue #70632's missing HTTP API-key header path. #51100 implements the focused manifest-derived fix, #59586 combines that fix with deAPI and broader catalog changes, #67682 supports validated explicit auth.env_var credentials plus Perseus Vault, and #70775 adds the header through a generated variable that can diverge from the credential saved from the manifest.

Related pull requests

Duplicates

#51100, the generic Bearer-auth portion of #59586, the generic portion of #67682, and #70775 overlap on the same _build_server_config omission; the deAPI and Perseus Vault catalog entries are distinct salvageable changes.

Suggested consolidation

Author action: rebase #70775 onto main and derive and validate the header variable from the declared manifest credential, following closed reference #51100, then add the contributor-requested noncanonical-name install test. This explicitly accounts for #70775's recorded best-fix status without overriding its contributor keep_open review; keep #59586 open only with a salvage path that splits out transport.headers and other independently reviewable additions, and keep #67682 open with a salvage path that drops the overlapping generic header hunk while retaining the validated Perseus Vault entry, treating both generic header portions as duplicates of the corrected #70775 path.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I70632(["issue #70632 (open)"])
    subgraph Dup51100 ["PRs duplicating each other"]
        P51100["PR #51100 (closed)"]
        P59586["PR #59586 (open)"]
        P67682["PR #67682 (open)"]
        P70775["PR #70775 (open)"]
    end
    P70775 -->|best fix| I70632
    class I70632 open
    class P51100 closed
    class P59586 open
    class P67682 open
    class P70775 open
    class P51100 best
    class P70775 best
    class P70775 target
    click I70632 "https://github.com/NousResearch/hermes-agent/issues/70632"
    click P51100 "https://github.com/NousResearch/hermes-agent/pull/51100"
    click P59586 "https://github.com/NousResearch/hermes-agent/pull/59586"
    click P67682 "https://github.com/NousResearch/hermes-agent/pull/67682"
    click P70775 "https://github.com/NousResearch/hermes-agent/pull/70775"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 4 pull requests and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 31 kB of PR diffs, 14 kB of issue/PR text, 10 kB of discussion (14 comments), 7 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data 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.

optional-mcps: http + api_key auth is prompted but never sent on catalog install

4 participants