npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fusedio/relay

v0.1.0

Published

JSON-UI bridge for agent↔human interaction — render structured questions and output as real UI (browser or inline in chat via MCP Apps) and get the answer back as JSON

Downloads

158

Readme

Relay

A JSON-UI bridge for agent↔human interaction. Relay lets an AI agent render a real UI to ask a human a structured question (radio, checkboxes, validated fields, forms) or show structured output (tables, charts) — and get the answer back as JSON. The agent authors a small JSON config; the human interacts with a live UI; the response is handed back to the agent.

Relay works in two environments from one product, sharing the same component catalog, config format, and result contract:

| Environment | How the UI appears | How the agent calls it | |---|---|---| | CLI agents (Claude Code, Cursor, Gemini, any shell/Bash tool) | A browser tab on localhost | relay render <config> — blocks, prints the answer as JSON on stdout | | Chat assistants (MCP Apps hosts: Claude Desktop, Claude Code, …) | Inline in the conversation (sandboxed iframe, via the MCP Apps standard) | the relay_render MCP tool — the human's response returns as a follow-up message |


Install

npm install -g relay

This installs two commands:

  • relay — the CLI used by shell/Bash agents and by you directly.
  • relay-mcp — the MCP server used by chat assistants (and local MCP hosts like Claude Desktop / Claude Code).

Prefer not to install globally? Every command also works through npx relay … / npx relay-mcp ….

Requirements: Node.js 18+. The CLI works in any shell/Bash agent; the inline chat experience needs an MCP Apps host (e.g. Claude Desktop or Claude Code).


Quick start (CLI)

Ask the human a question — the answer prints to stdout as JSON:

echo '{"type":"radio","props":{"param":"env","options":["staging","prod"]}}' \
  | relay render - --title "Deploy where?"
# opens a browser tab; after the human picks:
# -> {"env":"prod"}

Show the human structured output — a table/chart with a "Done" button:

relay render dashboard.json
# -> {}   (returned once the human clicks Done)

Discover a component's props before authoring it:

relay schema table

Because the Bash tool blocks until the command exits, the JSON answer lands directly back in the agent's context — no polling, no second call.


The config format

The agent authors a nested node: { "type", "props", "children" }.

  • Every input has a param prop. It binds to the data model, and that data model is the response: { "<param>": <value>, … }.
  • A Submit button (or Done, for displays with no inputs) is appended automatically if you don't include one.
  • validation and visible props are passed straight through to the renderer.
  • options accept ["a","b"] or [{ "label": "...", "value": "..." }].
{
  "type": "form",
  "props": { "title": "Pick a deploy target" },
  "children": [
    {
      "type": "radio",
      "props": {
        "param": "target",
        "label": "Environment",
        "options": ["staging", "prod"],
        "validation": { "checks": [{ "fn": "required", "message": "Choose one" }] }
      }
    },
    { "type": "text-area", "props": { "param": "notes", "label": "Notes (optional)" } }
  ]
}

→ response: { "target": "prod", "notes": "…" }


Component catalog

Fourteen self-contained components. Run relay schema <component> (or the relay_schema MCP tool) for the exact, always-in-sync props of any one.

| Type | Purpose | |---|---| | text | Markdown / plain text, heading variants | | form / stack | Containers; vertical layout, optional title | | text-input | Single-line string | | text-area | Multi-line string | | number-input | Numeric, with min / max | | select | Dropdown (single value) | | radio | Single choice | | checkbox-group | Multi choice → array | | slider | Bounded numeric (min / max / step) | | button | Fires submit or cancel | | table | Tabular display (columns, rows) | | bar-chart / line-chart | Charts (data, x, y, title) |


Result contract & exit codes (CLI)

relay render writes only the result JSON to stdout (URLs, logs, and prompts go to stderr) and maps the outcome to an exit code so the agent can branch:

| Exit | Meaning | stdout | |---|---|---| | 0 | Submitted | the data model, e.g. {"target":"prod"} | | 2 | Cancelled (human clicked Cancel) | {"__cancelled": true} | | 3 | Timed out (no response within --timeout) | {"__timeout": true} | | 4 | Closed (browser tab closed without submitting) | {"__closed": true} |

Relay detects a closed tab (a pagehide beacon to the server) and ends promptly with exit 4 instead of waiting out --timeout. Caveat: a page reload also fires pagehide, so reloading the tab ends the interaction as closed — distinguishing reload from close is intentionally not implemented.


CLI command reference

relay render <config.json | ->

Read a config from a file or - (stdin), serve it on 127.0.0.1, block until the human responds, print the response JSON, exit.

  • --title "…" — header / browser-tab title.
  • --timeout <sec> — default 600. A single clock governs both how long the command blocks and the server's maximum lifetime (no process lingers past it).
  • --no-open — print the URL only; don't auto-open the browser.

relay schema [<component>]

Print JSON Schema to stdout — all components, or one named component. This is the harness-agnostic, on-demand schema accessor.

relay skill install | list | uninstall

Teach the AI harnesses on your machine how to use Relay (see Harness integrations below).

  • --harness claude,agents,cursor,gemini,mcp — non-interactive selection.
  • --global (default) / --project — install scope.
  • --yes — skip the interactive checklist.

Using Relay from a chat assistant (MCP Apps)

In chat, Relay runs as a local MCP server and renders inline using the official MCP Apps standard: the tool declares a UI resource, the host fetches it (resources/read) and renders it in a sandboxed iframe inside the conversation, and the iframe talks to the host over a postMessage channel. This works zero-infrastructure over local stdio — no HTTP server, tunnel, or remote connector required.

1. Install the MCP server into your host

relay skill install --harness mcp            # writes ./.mcp.json (project) ...
relay skill install --harness mcp --global   # ... or ~/.mcp.json (global)

This adds a local stdio entry pointing at the server:

{ "mcpServers": { "relay": { "command": "relay-mcp", "args": ["--stdio"] } } }

Supported in MCP Apps hosts — Claude Desktop, Claude Code, and others. Restart (or "Reload MCP Configuration" in) the host so it picks up the server.

2. The agent calls the tools

The MCP server exposes two tools:

  • relay_render { config, title? } → renders the config inline in the chat as an MCP App. This is the only tool the agent calls to ask a question or show output. The human's response is delivered back to the agent as a follow-up message when they submit.
  • relay_schema { component? } → the same schema accessor as the CLI's relay schema, for authoring configs on demand.

How the chat round-trip works

agent ── relay_render(config) ──▶  host fetches the ui:// resource, renders the
                                    iframe inline, and pushes the config to it
human fills it in & submits
  iframe ──(sendMessage)──▶  the response (e.g. {"target":"prod"}) arrives as a
                             message; the agent continues

Because the UI HTML is delivered via resources/read (not in the tool result), the host's ~1 MB tool-result limit doesn't apply. The response mirrors the CLI: a submit yields the data model; a cancel yields { "__cancelled": true }.


Harness integrations (relay skill install)

relay skill install materializes one canonical doc set into each harness's native format, so your agents know when and how to reach for Relay. Adding a new harness is one adapter; nothing else changes.

| Harness (--harness id) | Detected by | What's written | |---|---|---| | claude (Claude Code) | ~/.claude/ or claude on PATH | Skill dir: SKILL.md + schemas/ | | agents (generic) | always (fallback) | AGENTS.md managed block | | cursor | .cursor/ or cursor on PATH | ./.cursor/rules/relay.mdc | | gemini | ~/.gemini/ or gemini on PATH | GEMINI.md managed block | | mcp (Claude Desktop/Code) | ~/.claude/, claude on PATH, or ./.mcp.json | .mcp.json server entry (idempotent merge) |

Markdown/JSON targets are written as idempotent managed blocks/merges — installing twice is a no-op, and uninstall removes only Relay's content (and drops a .mcp.json it solely created).

relay skill list shows what's detected; relay skill uninstall removes it.


How it works (architecture)

The browser host depends on an injected RelayTransport (loadConfig / submit) — the single seam that differs between environments. One bundle ships everywhere and picks its transport at runtime:

  • HTTP transport (CLI): a zero-dependency Node server on 127.0.0.1 serves the config and collects POST /result.
  • MCP Apps transport (chat): the host fetches the bundle via resources/read and renders it in a sandboxed iframe; inside it, the bundle uses the @modelcontextprotocol/ext-apps App client to read the config (ontoolinput) and return the answer (sendMessage).

Everything else — the component registry, the config compiler, validation, rendering — is shared, transport-agnostic code. The component schema is the single source of truth feeding the renderer, relay schema, the relay_schema tool, and the generated skill docs.

Security: the CLI server binds to 127.0.0.1 only; the config is data, never code (no eval, no dynamic component loading); markdown is rendered safely; and in chat the UI runs in the host's sandboxed iframe, isolated from the conversation page.

Design and implementation notes live in docs/superpowers/.


Contributing

npm install
npm run build     # bundle the host → dist/ + schemas + the self-contained resource
npm test          # vitest
npm run typecheck # tsc --noEmit

The component registry under src/registry/ is the one place to add a component: a React renderer + a Zod schema with .describe() on every prop. After registry changes, run npm run build (regenerates schemas) then npm test.

For the full local-development workflow — running the CLI and the MCP server from your checkout without publishing, wiring Relay into Claude Desktop / Claude Code, the build internals, and testing conventions — see CONTRIBUTING.md.