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

@swisper.ai/cards-sdk-client

v0.0.3

Published

Readme

@swisper.ai/cards-sdk-client

npm license

Define <swi-*> cards, and their lifecycle. The host app supplies the message renderer and a CardProvider that connects cards to the conversation.

Boundary

Code belongs here if it defines a <swi-*> card or connects cards to a host: the card() call, the hooks a card uses, the action wire format, the canvas, and the providers a host mounts. Cards themselves, their styles, and the host's message renderer belong in the app or bundle that ships them.

Install

npm install @swisper.ai/cards-sdk-client

| Peer | Range | Why | | ----------- | -------- | -------------------- | | react | 18.3.1 | Components and hooks | | react-dom | 18.3.1 | Rendering | | zod | ^4.2.0 | Card schemas |

@module-federation/runtime and the @modelcontextprotocol/* packages are regular dependencies and install with the package. Only the /host, /mcp-apps-host and /mcp-apps-sandbox entry points import them.

Setup

  1. Define a card once, at module scope:
import { card, type SwiCardComponent } from '@swisper.ai/cards-sdk-client';
import { z } from 'zod/v4';

const weatherSchema = z.object({ city: z.string(), degrees: z.number() });

const Weather: SwiCardComponent<z.infer<typeof weatherSchema>> = ({ data }) => (
  <p>
    {data.city}: {data.degrees}°
  </p>
);

export const SwiWeatherCard = card({
  name: 'swi-weather',
  schema: weatherSchema,
  Component: Weather,
});
  1. Hand the host's cards to CardsProvider. Pass a module-level array, so useCards() stays stable:
import { CardsProvider } from '@swisper.ai/cards-sdk-client';

const cards = [SwiWeatherCard];

<CardsProvider cards={cards}>
  <Chat />
</CardsProvider>;
  1. In the message renderer, mount the cards from useCards() and wrap each one in CardProvider, which supplies what useCard() returns for that card's message.

API

Defining cards

| Export | Why it exists | | ------------------ | ------------------------------------------------------------------------------------------------- | | card | Turns a name, schema and component into a card the renderer can match, parse, validate and mount. | | DefaultCardError | The fallback shown when a card fails, without leaking the payload onto the screen. |

A card also exposes Renderer (your component, for tests and stories) and parse(raw) (the same parse-and-validate path, for showing a card's data outside the message list; it never throws).

Inside a card

| Export | Why it exists | | ---------------------- | --------------------------------------------------------------------------------- | | useCard | What the host lets a card do: send actions, build tags, open the canvas. | | EditableCardProvider | Shares one card's edit mode with every part of that card. | | useEditableCard | Reads that shared edit mode. | | useEditableCardState | Edit mode for a single component; every transition is a no-op while not editable. |

Actions

| Export | Why it exists | | --------------------------- | --------------------------------------------------------------------------- | | cardAction | Writes an action as a swisper:// markdown link, the whole wire format. | | encodeCards | Joins several tags so the renderer doesn't read them as one card. | | extractCardActionFromLink | Reads the first action link back out of message text. | | isCardActionURL | Tells the host's markdown renderer which links to render as buttons. | | useCardActions | Turns a list of links and plain replies into actions a card can render. | | CARD_ACTION_PROTOCOL | The swisper scheme, for allow-listing it in the host's markdown renderer. |

Host

| Export | Why it exists | | ------------------------ | ------------------------------------------------------------------------------------------- | | CardProvider | Wraps each card with what useCard() returns for that card's message. | | CardsProvider | Registers the host's own cards. | | useCards | Every available card, remote first then local; throws if two claim the same tag. | | RemoteCardsProvider | Loads cards from a separately deployed module-federation bundle and exposes its load state. | | useRemoteCards | Reads the remote bundle's load state; throws outside RemoteCardsProvider. | | usePossibleRemoteCards | The same, but null outside the provider, for code that works with or without a remote. |

Canvas

| Export | Why it exists | | -------------------- | ---------------------------------------------------------------------------------------------- | | CanvasProvider | Owns the panel outside the chat that cards open: its stack of contents, width and full screen. | | CanvasContent | Renders a card's content into the canvas while its id is stacked, keeping its state. | | useCanvasContext | Opens, closes and resizes the canvas. | | useCanvasLifecycle | Tells a component when the canvas starts and stops showing its id. | | useCanvasResize | Turns an element into an accessible resize handle (drag, arrow keys, double-click to reset). |

Types for all of the above (SwiCard, SwiCardComponent, SwiCardName, SwiCardAction, SwiCardErrorProps and the rest) are exported alongside them.

Troubleshooting

| Symptom | Cause | Fix | | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | useCard() called outside CardProvider | The card isn't wrapped in CardProvider, or the host and a remote bundle each loaded their own copy | Wrap each card the message renderer mounts in CardProvider. With module federation, list this package as a shared singleton in both build configs. | | useCanvasContext() called outside CanvasProvider | Same as above, for the canvas | Mount CanvasProvider in the host, and share this package as a singleton. | | The card shows "Something went wrong." | The tag body didn't parse or match the schema, or the component threw | Check the console for could not parse its body or threw while rendering; the element's data-swi-card-error says which. | | JSON tag body fails to parse | The tag has no data-format, so the body is read as CSV | Add data-format="json" to the tag. | | Type error: "weather" is not assignable to swi-${string} | Card names must start with swi- | Rename the card, e.g. swi-weather. | | "swi-…" is provided more than once. | The remote bundle and CardsProvider both define that tag | Remove one of them, or rename it. | | did not default-export an array | The remote's exposed module isn't the card list | Default-export an array of card() results from the module named by exposedModule (default ./registry). | | bundleUrl is not set | RemoteCardsProvider got an empty bundleUrl | Pass the URL of the remote's remoteEntry.js. | | Action links render as plain text, or are stripped | The host's markdown renderer drops unknown URL schemes | Allow swisper: in its URL transform and render links where isCardActionURL(href) is true as buttons. | | Two cards sent in one action render as one broken card | Back-to-back tags were concatenated | Join them with encodeCards([first, second]). | | Console warns Card is not editable | startEdit, discard or toggleEdit was called while isEditable is false | Pass isEditable={useCard().isActionEnabled}; it is false on older messages by design. | | CanvasContent never appears | CanvasContent doesn't open the canvas, or the host never attached containerRef | Call openInCanvas(id) (or useCanvasContext().onOpen(id)), and attach containerRef to the canvas element. | | "Invalid hook call", or a context is missing | The app resolves two copies of React | Add resolve: { dedupe: ['react', 'react-dom'] } to the Vite config |

License

Apache-2.0