Skip to content

feat(memory): Graphnosis provider UI, MCP install surfaces, and onboarding#763

Open
nehloo wants to merge 1 commit into
fathah:mainfrom
nehloo-interactive:feat/graphnosis-memory-and-mcp-ui
Open

feat(memory): Graphnosis provider UI, MCP install surfaces, and onboarding#763
nehloo wants to merge 1 commit into
fathah:mainfrom
nehloo-interactive:feat/graphnosis-memory-and-mcp-ui

Conversation

@nehloo

@nehloo nehloo commented Jun 23, 2026

Copy link
Copy Markdown

Summary

Adds Graphnosis UI surfaces to Hermes Desktop — companion to the Hermes Agent integration in NousResearch/hermes-agent#51197.

Surface What users get
Memory providers English description + link to graphnosis.com/download
Capabilities → MCP Servers Install promo (hermes mcp install graphnosis) + app download
Discover → MCPs Bundled Graphnosis catalog entry when optional-mcps/graphnosis exists in hermes-agent
Welcome Get Connected onboarding card

Memory provider discovery is automatic once the hermes-agent Graphnosis plugin is installed.

Integration source: https://github.com/nehloo-interactive/graphnosis-app/tree/main/integrations/hermes-desktop

Depends on

Changes

  • listBundledMcps() IPC + Discover tab integration (mirrors bundled skills pattern)
  • Memory provider i18n + download URL
  • Tools MCP install promo card
  • Welcome onboarding card + shared styles

Test plan

  • npm run typecheck — passes
  • npm test -- tests/preload-api-surface.test.ts — passes
  • Memory screen shows Graphnosis card when hermes-agent plugin is installed
  • Capabilities → MCP shows Graphnosis promo when server not installed; Install runs hermes mcp install graphnosis
  • Discover → MCPs lists Graphnosis when optional-mcps/graphnosis/manifest.yaml present
  • Welcome screen shows Graphnosis onboarding card with download link

Maintained by Nehloo Interactive LLC (nehloo-interactive/graphnosis-app).

Made with Cursor

…rding

Add English copy and download links for the Graphnosis memory provider,
a Capabilities MCP install promo (hermes mcp install graphnosis), Discover
bundled MCP catalog entry when optional-mcps/graphnosis exists in
hermes-agent, and a Welcome onboarding card pointing to graphnosis.com.

Depends on NousResearch/hermes-agent Graphnosis memory + MCP PR.
Integration source: nehloo-interactive/graphnosis-app integrations/hermes-desktop/

Co-authored-by: Cursor <cursoragent@cursor.com>
@nehloo nehloo force-pushed the feat/graphnosis-memory-and-mcp-ui branch from 8b075b5 to 7c2d91e Compare June 30, 2026 00:09
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds Graphnosis (a local encrypted memory provider) UI surfaces across four screens in Hermes Desktop: a memory provider entry, a Tools MCP promo card, a Discover tab catalog entry, and a Welcome onboarding card. It also introduces a new listBundledMcps IPC channel that mirrors the existing listBundledSkills pattern, gated on the presence of optional-mcps/graphnosis/manifest.yaml in the hermes-agent repo.

  • listBundledMcps IPC + Discover integration: New IPC handler returns [] for SSH connections and a hardcoded Graphnosis RegistryItem when the upstream manifest file exists; bundled MCPs are merged into the Discover MCPs tab with the same id+name deduplication used for bundled skills.
  • Tools promo card: Shown only when the Graphnosis MCP server is not yet installed; triggers installMcpCatalogEntry("graphnosis", …) directly without going through the registry item path.
  • Memory + i18n: Adds the graphnosis URL to PROVIDER_URLS and English strings across memory.ts, tools.ts, and welcome.ts.

Confidence Score: 4/5

Safe to merge; changes are additive UI surfaces with no modifications to existing install or MCP logic paths.

All new code is additive — new IPC channel, new UI cards, new i18n strings. The two issues found are both minor: a wrapper div in Welcome.tsx carries a class name that has no corresponding CSS rule, and the MCP tab-badge count in Discover.tsx duplicates dedup logic that's already computed in a memo, meaning the two could silently diverge if one is updated without the other. Neither affects functionality today.

src/renderer/src/screens/Discover/Discover.tsx (duplicated dedup logic in tabCount) and src/renderer/src/screens/Welcome/Welcome.tsx (missing welcome-graphnosis-copy CSS class).

Important Files Changed

Filename Overview
src/main/registry.ts Adds listBundledMcps() which checks for optional-mcps/graphnosis/manifest.yaml before returning the hardcoded catalog entry, and routes catalog-keyed registry items through installMcpCatalogEntry. Both paths are clean.
src/main/ipc/register.ts Registers list-bundled-mcps IPC handler; returns [] for SSH connections (intentional — Graphnosis is a local service) and delegates to listBundledMcps() otherwise.
src/renderer/src/screens/Discover/Discover.tsx Integrates bundled MCPs into the Discover tab with deduplication. The tabCount for MCPs duplicates the dedup logic from communityList without memoization, creating a potential divergence point unlike the memoized skillsTotal used for skills.
src/renderer/src/screens/Tools/Tools.tsx Adds a Graphnosis MCP promo card (hidden once installed) with install and download actions. Follows existing installMcpCatalogEntry patterns and properly guards with !graphnosisMcpInstalled.
src/renderer/src/screens/Welcome/Welcome.tsx Adds an unconditional Graphnosis onboarding card to the initial welcome panel. The welcome-graphnosis-copy wrapper class used in JSX has no corresponding CSS rule in main.css.
src/renderer/src/assets/main.css Adds .welcome-graphnosis-card/title/body and .tools-graphnosis-promo/promo-actions styles. Missing the .welcome-graphnosis-copy class referenced in Welcome.tsx.
src/shared/registry.ts Adds optional catalog field to RegistryItem interface with a JSDoc comment. Non-breaking additive change.
src/preload/index.ts Exposes listBundledMcps on the preload API surface, correctly typed and delegating to the list-bundled-mcps IPC channel.
src/preload/index.d.ts Type declaration for listBundledMcps matches the preload implementation exactly.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant R as Renderer
    participant P as Preload
    participant M as Main (IPC)
    participant FS as File System

    R->>P: listBundledMcps()
    P->>M: ipcRenderer.invoke("list-bundled-mcps")
    M->>FS: existsSync(optional-mcps/graphnosis/manifest.yaml)
    FS-->>M: exists / not found
    M-->>P: RegistryItem[] (or [])
    P-->>R: bundledMcpList

    R->>R: merge bundledMcps into communityList (deduped)
    R->>R: show Graphnosis entry in Discover MCPs tab

    R->>P: "installMcpCatalogEntry("graphnosis", {}, profile)"
    P->>M: ipcRenderer.invoke("install-mcp-catalog-entry")
    M-->>P: "{ success, error?, background? }"
    P-->>R: result
    R->>R: reloadMcp() on success
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant R as Renderer
    participant P as Preload
    participant M as Main (IPC)
    participant FS as File System

    R->>P: listBundledMcps()
    P->>M: ipcRenderer.invoke("list-bundled-mcps")
    M->>FS: existsSync(optional-mcps/graphnosis/manifest.yaml)
    FS-->>M: exists / not found
    M-->>P: RegistryItem[] (or [])
    P-->>R: bundledMcpList

    R->>R: merge bundledMcps into communityList (deduped)
    R->>R: show Graphnosis entry in Discover MCPs tab

    R->>P: "installMcpCatalogEntry("graphnosis", {}, profile)"
    P->>M: ipcRenderer.invoke("install-mcp-catalog-entry")
    M-->>P: "{ success, error?, background? }"
    P-->>R: result
    R->>R: reloadMcp() on success
Loading

Reviews (1): Last reviewed commit: "feat(memory): Graphnosis provider UI, MC..." | Re-trigger Greptile

Comment on lines +398 to +403
<div className="welcome-graphnosis-copy">
<div className="welcome-graphnosis-title">
{t("welcome.graphnosisTitle")}
</div>
<p className="welcome-graphnosis-body">{t("welcome.graphnosisBody")}</p>
</div>

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.

P2 The welcome-graphnosis-copy class is used here but is not defined in main.css. The other card classes (.welcome-graphnosis-card, .welcome-graphnosis-title, .welcome-graphnosis-body) are all present in the stylesheet, so this wrapper div is the odd one out. It won't break layout because the parent flex container provides the gap, but the missing class makes the intent of this wrapper ambiguous and it's inconsistent with the rest of the named classes.

Suggested change
<div className="welcome-graphnosis-copy">
<div className="welcome-graphnosis-title">
{t("welcome.graphnosisTitle")}
</div>
<p className="welcome-graphnosis-body">{t("welcome.graphnosisBody")}</p>
</div>
<div>
<div className="welcome-graphnosis-title">
{t("welcome.graphnosisTitle")}
</div>
<p className="welcome-graphnosis-body">{t("welcome.graphnosisBody")}</p>
</div>

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines 261 to 275
function tabCount(key: RegistryKind): number {
if (key === "skills") return skillsTotal;
if (key === "mcps") {
const list = catalog.mcps ?? [];
const seen = new Set([
...list.map((i) => i.id),
...list.map((i) => i.name),
]);
const extra = bundledMcps.filter(
(b) => !seen.has(b.id) && !seen.has(b.name),
);
return list.length + extra.length;
}
return (catalog[key] ?? []).length;
}

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.

P2 Duplicated dedup logic, not memoized

tabCount for "mcps" re-implements the exact same id+name deduplication that already lives in the communityList useMemo. skillsTotal handles the skills equivalent via a dedicated useMemo so the count and the list stay in sync by construction — the MCP case doesn't have that guarantee. If the dedup logic is ever adjusted in communityList, the count displayed on the tab badge will silently diverge. Consider extracting a mcpsTotal memo analogous to skillsTotal, or reading communityList.length directly (it's computed before tabCount is called).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant