nimblebraininc/skills · Archived

synapse

Build a Synapse UI for an MCP server — a React app built to one inlined HTML file, served as a `ui://` resource and rendered in the NimbleBrain host.

First seen Jun 22, 2026

Installation

$ npx skills add nimblebraininc/skills --skill synapse

Summary

  • Build a Synapse UI for an MCP server — a React app built to one inlined HTML file, served as a `ui://` resource and rendered in the NimbleBrain host.
  • Works with any MCP server (Python/FastMCP or TypeScript).
  • Use when building a Synapse app or UI, adding a frontend to an MCP server, making a server "visual" or "interactive", or wiring a `ui://` resource.

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from nimblebraininc/skills.

npx skills add nimblebraininc/skills

Browse all from nimblebraininc/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 3
License MIT
Default branch main
Open issues 8
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

Version0.1.0
LicenseMIT
CompatibilityNode.js 22+, npm (for the React/Vite UI build). The MCP server itself can be Python (FastMCP) or TypeScript.
Allowed toolsRead Write Bash Glob Grep WebFetch
More metadata
area
synapse
version
0.1.0
author
NimbleBrain

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,645 B
  • docs SUMMARY.md 372 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 35 installs

SKILL.md

Synapse — build a UI for an MCP Server

Give an MCP server an interactive UI. The UI is a React app built to one inlined HTML file with @nimblebrain/synapse + Vite, served by the server as the MCP resource ui://<name>/main, and mounted by the NimbleBrain host in a sandboxed iframe wired to a postMessage bridge. The UI calls the server's existing tools over that bridge — it is data-layer-agnostic and needs no special server framework.

Target @nimblebrain/synapse@^0.13.0 (published on npm). The package is the documentation — read its exported types before writing code.

Pre-flight — read the SDK's types (the real docs)

From the installed package (node_modules/@nimblebrain/synapse/dist/*.d.ts) or the public repo (github.com/NimbleBrainInc/synapse):

  • the react entry — hooks (useSynapse, useCallTool, useDataSync, useTheme, useVisibleState, …).
  • the ui entry — the component library (AppFrame, ListDetailLayout, ListRow, Table, Badge, Prose, tokens, …). Don't hand-roll styling.
  • the type exports — ToolCallResult, SynapseTheme, the Synapse interface.

Then read references/gotchas.md (non-obvious API facts that each save a debugging cycle) and references/host-contract.md (the manifest + bridge contract). Skim these first; they're short and they're the difference between working and "why is every tool call returning unauthenticated."

Process

  1. Analyze the server — language (Python/FastMCP or TS), transport (stdio vs HTTP-native/edge-fronted), the tool list + return shapes, and deploy shape (.mcpb bundle vs a container image — the container needs a Node build stage, step 7).
  1. Scaffold ui/package.json (react/react-dom ^19, @nimblebrain/synapse@^0.13.0, vite, vite-plugin-singlefile, typescript; add marked + dompurify only if you render markdown), vite.config.ts (react(), viteSingleFile(), synapseVite(), build.assetsInlineLimit: Infinity), a strict tsconfig.json, index.html, and .gitignore (node_modules/, dist/, .vite/). Commit package-lock.json so the build can npm ci.
  1. Build App.tsx<SynapseProvider name="<server>">. One side-effect import goes in the Vite entry (main.tsx): import "@nimblebrain/synapse/ui/base" (the root-height chain AppFrame fills — applied before first paint; gotcha M). Don't import fonts — the SDK ships none, and typography arrives from the host like every other theme value (gotcha N). Use the package's AppFrame shell (with AppFrame.Body bleed hosting ListDetailLayout/SidebarLayout) — never a hand-rolled height:100vh (gotcha D). Drive data with a thin useCall<T>() wrapper around useSynapse().callTool(name, args); refresh with useDataSync; theme with tokens/useTheme; push agent context with useVisibleState. Master lists use ListRow, not Table (gotcha D — a Table overflows a fixed-width rail and paints over the detail pane).
  1. Sanitize any rendered HTML — do not skip (stored-XSS). If you render server- or agent-authored markdown (notes, descriptions, research output) via Prose / dangerouslySetInnerHTML, run it through DOMPurify first: DOMPurify.sanitize(marked.parse(md, { async: false }) as string). The iframe runs with allow-scripts and a script-src 'unsafe-inline' CSP, so sanitization — not the CSP — is the only thing stopping an injected <script>/onerror from running with full tool-bridge authority (read/exfiltrate/mutate everything the tools can reach). Plain <Text>{value}</Text> is safe (React escapes). Full chain: gotcha K.
  1. Serve the UI as a resource@mcp.resource("ui://<name>/main", mimetype="text/html") returning the built ui/dist/index.html. Resolve the path via an env var (<APP>UI_DIR) with a file-relative fallback (gotcha E — an installed package lands in site-packages, so a file-relative ui/dist lookup misses). Make it a bare file read: no DB/auth session, identity-free HTML, all tenant data fetched at runtime through the bridge.
  1. Declare the host placement — add _meta["ai.nimblebrain/host"] to manifest.json: one placements[] entry (slot: "sidebar.apps", resourceUri: "ui://<name>/main", route, label, icon). Full contract + options: references/host-contract.md.
  1. Container-deployed servers — multi-stage build. A .mcpb bundle gets ui/dist from release CI; a container image must build it: a node:22 builder stage runs npm ci && npm run build, then the runtime COPY --from=builder …/ui/dist (so the runtime stays Node-free) and sets the UI-dir env var. Strip the ui/ source from the runtime layer — only dist ships.
  1. Local previewcd ui && npm run dev → open /__preview. For an edge-fronted server (identity from HTTP headers + a DB), the real server can't be driven over the stdio preview, so every call returns unauthenticated — point synapseVite({ serverCmd }) at a seeded stdio mock behind an env flag (gotcha F).
  1. Verifynpx tsc --noEmit and npm run build (Vite/esbuild won't type-check on its own). Then run the server project's own lint/format/test gate (e.g. make verify, ruff format --check), not just the UI type-check — a format-only diff will redden CI even when types pass. Toggle the preview between light and dark and eyeball every surface — theme-blind color passes tsc/build and only breaks in one mode. Every tokens.* is backed in both; your own vars are not (gotcha L). Confirm the server serves ui://<name>/main as text/html.

Out of scope

  • Host-side rendering — the host already implements placement → iframe → bridge; you build the bundle + declare the placement, nothing more.
  • Declarative host "commands"/toolbars — they don't exist. In-app buttons live inside the iframe and call tools via useCallTool.