Skip to content

feat(integrations): use Hermes native MCP runtime for Basic Memory #1475

Description

@phernandez

Summary

Connect the Basic Memory memory provider through Hermes's native MCP runtime instead of maintaining a provider-owned MCP actor.

This is the implementation-specific follow-up to #1387 and replaces the transport approach in #1397. The user-facing goal remains the same: Hermes should support automatic Basic Memory recall and capture against an existing remote Streamable HTTP MCP endpoint.

Why this direction

Hermes v0.21.0 / v2026.8.31 and current upstream main provide PluginContext.call_mcp(server, tool, arguments, timeout=...). It is synchronous for plugin hooks and handlers, but executes through Hermes's existing MCP client and connection lifecycle:

  • stdio and Streamable HTTP transports
  • static headers and OAuth
  • reconnect and session-expiry recovery
  • circuit breaking and timeouts
  • trust gates
  • shared tool discovery and registration

Using that surface gives Hermes one owner for MCP connections. The Basic Memory provider remains responsible for memory policy—prefetch, automatic turn capture, session summaries, and commands—without duplicating transport state, retry rules, or an asyncio actor.

Relevant Hermes sources:

Proposed design

Configure Basic Memory once as a normal Hermes MCP server.

Remote:

memory:
  provider: basic-memory

mcp_servers:
  basic_memory:
    url: "https://memory.example.com/mcp"
    headers:
      Authorization: "Bearer ${BASIC_MEMORY_API_KEY}"

plugins:
  entries:
    basic-memory:
      mcp_allowlist:
        - basic_memory

Local stdio uses the same path:

mcp_servers:
  basic_memory:
    command: "bm"
    args: ["mcp"]

The provider receives a narrow callable backed by:

ctx.call_mcp("basic_memory", tool_name, arguments, timeout=timeout)

Provider lifecycle operations call the native MCP tools they need. The model receives the complete Basic Memory MCP tool surface through Hermes's normal discovery.

Expose the complete tool surface

Do not maintain a second, limited set of bm_* schemas.

  • Return [] from BasicMemoryProvider.get_tool_schemas().
  • Do not configure mcp_servers.basic_memory.tools.include or .exclude by default.
  • Let Hermes register all Basic Memory tools, resources, and prompts directly.
  • Retain the per-server plugins.entries.basic-memory.mcp_allowlist; it authorizes provider-internal calls and is separate from model-facing tool filtering.

This also means newly added Basic Memory MCP tools become available without updating the Hermes provider.

Hermes prerequisite

There is one small upstream gap. The exclusive memory-provider _ProviderCollector delegates register_* methods to a real PluginContext, but currently hides non-registration capabilities such as call_mcp().

Submit an upstream Hermes change that explicitly forwards _ProviderCollector.call_mcp(...) to its lifecycle-owned PluginContext. Avoid reaching into Hermes's private MCP registries or calling _make_tool_handler from Basic Memory.

Set an explicit minimum Hermes version containing that forwarding API. Prefer a clear compatibility floor over keeping two independent MCP implementations indefinitely.

Acceptance criteria

  • A supported Hermes release exposes call_mcp() to exclusive memory-provider registration.
  • Remote endpoint URL and authentication live in Hermes's canonical mcp_servers.basic_memory configuration.
  • Local stdio and remote HTTP use Hermes's native MCP lifecycle.
  • The Basic Memory provider owns no MCP event loop, transport session, subprocess, reconnect logic, or actor.
  • Automatic prefetch, turn capture, session summaries, and provider commands work through ctx.call_mcp().
  • All Basic Memory MCP tools are available through native Hermes discovery with no default include list.
  • The limited duplicate bm_* tool surface is removed.
  • Clear integration tests start a real Basic Memory MCP endpoint and prove:
    • Hermes discovers the complete Basic Memory tool surface.
    • provider prefetch reads through the native connection.
    • automatic turn capture writes through the same native connection.
    • a transport reconnect does not create a second provider-owned session.
  • Tests are scenario-focused integration tests rather than parameterized transport matrices.
  • Hermes package documentation shows both local stdio and remote HTTP configuration.

Out of scope

  • Reimplementing Hermes authentication, retry, reconnect, or circuit-breaker behavior.
  • A second MCP SDK client owned by the Basic Memory provider.
  • Restricting the model to a curated subset of Basic Memory tools by default.

Activity

  1. claude commented on Sep 4, 2026

    @claude
    Contributor

    Claude finished @phernandez's task in 33s —— View job


    Triage

    Type: Enhancement — architectural change to how the Hermes memory-provider integration reaches Basic Memory's MCP surface (routes through Hermes's native PluginContext.call_mcp instead of a provider-owned MCP actor/transport).

    Component: integrations/hermes (Hermes plugin package). Not a Cloud-specific issue — applies to both local stdio (bm mcp) and remote Streamable HTTP configurations.

    Priority: Medium. This is a design/maintainability improvement (removing duplicated transport/retry/reconnect logic from the provider) rather than a fix for broken user-facing functionality today.

    Complexity: Complex. It depends on an upstream Hermes change (_ProviderCollector.call_mcp forwarding) that doesn't exist yet, requires pinning a minimum Hermes version once that lands, removing the current bm_* tool-schema surface (get_tool_schemas() → []), and new integration tests against a real Basic Memory MCP endpoint covering discovery, prefetch, turn capture, and reconnect behavior without a second session. Sequencing (upstream PR first, then this repo's changes) is a key risk to plan around.

    Relationship to other issues: Not a duplicate. This is the stated "implementation-specific follow-up" to #1387 (remote-projects support in the Hermes plugin, still open) and explicitly replaces the transport approach from #1397 (closed). No other open issue covers this native-MCP-runtime approach.

    Note on scope: The issue itself is thorough (design, acceptance criteria, out-of-scope list) — no clarifying questions needed before implementation planning. Per triage process, no code changes were made here.

  2. added this to the v0.24.0 milestone on Sep 8, 2026
  3. phernandez commented on Sep 8, 2026

    @phernandez
    MemberAuthor

    The upstream prerequisite is now submitted: NousResearch/hermes-agent#105558. It explicitly forwards memory-provider ctx.call_mcp through the existing PluginContext, retaining native MCP lifecycle and per-plugin allowlists. A real discovery/config regression fails on upstream main and passes with the fix; 24 targeted tests pass. Current Hermes main still lacks this forwarding, so the downstream migration remains gated on an upstream release containing it. No second MCP client or private-registry workaround is being added.

  4. phernandez commented on Sep 14, 2026

    @phernandez
    MemberAuthor

    Rechecked for v0.24.0 on 2026-09-14: blocked on a released Hermes prerequisite; downstream reimplementation stopped before coding.

    Concrete upstream evidence:

    • Latest published release is v2026.9.11, published 2026-09-11, commit 939e45c91d751fadd94dcd1b873ac3cb44846213.
    • That release has PluginContext.call_mcp, including native MCP dispatch and per-plugin allowlist enforcement. However, its _ProviderCollector has no call_mcp method, and __getattr__ explicitly raises for every non-register_* attribute.
    • Current upstream main 5eb99eb2844b22ebb723711b8e6a0bbb80bb5f04 has the same missing forwarding.
    • Upstream prerequisite PR #105558 is still OPEN, with mergedAt: null and no merge commit. Its proposed implementation forwards through the collector's existing lifecycle-owned context.

    Verification: downloaded the release and main source through the GitHub API and executed each unchanged _ProviderCollector class in isolation, extracted with Python AST (no replacement collector or transport mock). Both produce:

    release: _ProviderCollector("basic-memory").call_mcp -> AttributeError: call_mcp
    main: _ProviderCollector("basic-memory").call_mcp -> AttributeError: call_mcp
    

    This is a narrow capability probe, not a full Hermes discovery or live MCP integration test. It directly confirms that the required binding in provider registration remains unavailable.

    Also inspected the current Basic Memory integration and abandoned PR #1397: current code still owns _BmMcpActor, a stdio ClientSession, and ten curated bm_* schemas; #1397 was closed unmerged in favor of this issue's host-owned transport design. Reviving that actor/HTTP design or accessing private Hermes registries would bypass the agreed prerequisite rather than complete #1475.

    Prepared fresh branch codex/1475-hermes-native-mcp from fetched origin/main at 756f183617a8aae0ce7c503b88e9fb5c8180b1de in the existing reusable basic-memory-pr1524 worktree. No implementation changes, commits, new downstream PR, or merge. Main checkout and worktree topology preserved. Package checks and live host contract tests were not run because implementation is gated before coding.

    Adding needs review. Resume when a supported Hermes release contains the forwarding API (or an equivalent public memory-provider capability), then establish that release as the compatibility floor and implement/test the native-runtime design. Issue remains open; it is not complete for v0.24.0.

  5. phernandez commented on Sep 14, 2026

    @phernandez
    MemberAuthor

    Milestone status (2026-09-14): blocked on a released Hermes prerequisite; no downstream code should be written yet.

    Fresh read-back is unchanged: the latest Hermes release remains v2026.9.11, and NousResearch/hermes-agent#105558 is still open. Both the release and current upstream collector lack the call_mcp forwarding required for a memory provider to use Hermes's lifecycle-owned native MCP runtime.

    Next step: when a supported Hermes release contains that forwarding API, set it as the compatibility floor, replace the provider-owned MCP actor with native ctx.call_mcp, and run discovery/prefetch/capture/reconnect tests against a real Basic Memory MCP endpoint. Do not revive the second-client design or depend on private registries.

  6. removed this from the v0.24.0 milestone on Sep 14, 2026
  7. phernandez commented on Sep 14, 2026

    @phernandez
    MemberAuthor

    Milestone decision: removing this from v0.24.0. Pi's integration model is intentionally CLI-first rather than MCP-native, and the Hermes-native MCP migration remains independently blocked on an unreleased upstream forwarding API. Keep this issue open as a future Hermes integration improvement; it is not a v0.24 release requirement.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthermesneeds reviewA human maintainer needs to look at this issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions