Card SDK

Build a card.Put it on every canvas.

A card is a small web app that lives on the NeuroSquad canvas next to your agents. Publish it on GitHub and anyone installs it by pasting the address. It can listen to agents, give them tools, send them prompts and trade typed data with other cards over arrows — with exactly the permissions the user grants.

Plain HTML and JavaScript, or React with Vite. The SDK has no runtime dependencies and is MIT-licensed.

Paste owner/repo

Agents

A card works alongside your agents

A card sees which agents are on the canvas and what they are doing. Connect one with an arrow, and the card can react to its turns, give it a tool or hand it the next task.

Reacts when an agent finishes

The card subscribes to agent events: status changes, the start and end of a turn, new output. Here the test radar reruns the suite every time the connected agent ends a turn.

card.agents.onTurn(({ agentId, phase }) => {  if (phase === 'end') runTests()})
Arrows

Cards talk over arrows, in typed data

A card declares its inputs and outputs with a type: text, Markdown, tasks, tables, events — or a type of your own with a schema. Draw an arrow and the value flows. The app checks it on both ends and converts it where a built-in card expects something else.

ns:tasks

Turn arrows on and off, then run the tests.

Built-in cards take part

A note appends Markdown, a checklist adds tasks, a task board adds cards, an agent takes a prompt, a terminal runs a command. Your card talks to all of them the same way.

Types, not guesswork

Well-known types (ns:text, ns:markdown, ns:tasks, ns:table, ns:event…) or your-card/your-type with a JSON schema. The app validates every value before it arrives.

Requests, not only streams

An input can answer: ports.request asks a connected card and waits for its reply. A retained output hands its last value to a card the moment you connect it.

Sandbox

Community code, on a short leash

Before anything installs you see who made it, where it comes from and at which commit, and in plain words what it will be able to do. After that, the app enforces exactly that.

Install Test radar?

CommunityVersion 1.0.0by Acme (self-declared)
Sourceacme/test-radar @ 3f9c2e1

Community code, not made or checked by NeuroSquad. Install cards only from people you trust.

Anything inside the card is the card’s own. NeuroSquad never asks for passwords or keys there.

This card will be able to

Run commands in terminals connected to it

High risk

Type and run commands in terminal cards you connect to it with an arrow — anything you could run yourself.

Read and change cards connected to it

Medium

Notes, checklists, task boards and stickies you connect to it with an arrow.

Offers tools to agents you connect it to: run_tests

Exchanges data over arrows: 1 in, 1 out

Sealed in its own frame

Each card runs in a sandboxed frame with its own origin. No access to the app, to Node, to other cards, or to your files beyond what you grant.

Never leaves the canvas

No popups, no new windows, no dialogs over the app, no downloads. A card draws only inside its own box and zooms with the canvas.

Only what you grant

Every call is checked in the app against the grant. Optional permissions are asked for later, on the card, and any permission can be revoked.

Arrows are consent

A card reaches another card, an agent or a terminal only if you drew an arrow between them — and only with the matching permission.

Network through a proxy

Only to the hosts the card declared. The app puts the secrets you enter into requests; the card never sees them.

Pinned, never auto-updated

An install is pinned to a commit. A new version shows what changed and which permissions it adds, and waits for your click.

Code

A whole card, one short file

This is a complete card. It runs the tests in a connected terminal, sends the failures down an arrow, offers agents a run_tests tool and reruns when a connected agent finishes. The manifest next to it says what it needs.

1import { connect } from '@neurosquad/card-sdk'2 3const card = await connect()4 5/** Runs the suite in the terminal this card is wired to and sends out what broke. */6async function runTests(filter = ''): Promise<string[]> {7  const shell = card.ports.peers.find((peer) => peer.kind === 'terminal')8  if (!shell) throw new Error('Draw an arrow from this card to a terminal')9 10  await card.setStatus('Running tests…', { busy: true })11  const { output } = await card.terminals.run(shell.cardId, `npm test -- ${filter}`)12  const failed = output.split('\n').filter((line) => line.includes('✗'))13 14  const tone = failed.length ? 'danger' : 'success'15  await card.setStatus(failed.length ? `${failed.length} failed` : 'Passing', { tone })16  // ns:tasks: a checklist adds them, a note gets a Markdown list17  await card.ports.emit('failures', failed.map((text) => ({ text, status: 'error' })))18  return failed19}20 21// Connected agents see this as test_radar_run_tests over MCP22card.tools.handle<{ filter?: string }>('run_tests', async ({ filter }, call) => {23  call.progress('running…')24  const failed = await runTests(filter)25  return failed.length ? failed.join('\n') : 'All tests passed'26})27 28// A connected agent finished its turn: check its work29card.agents.onTurn(({ agentId, phase }) => {30  const wired = card.ports.peers.some((peer) => peer.cardId === agentId)31  if (phase === 'end' && wired) runTests().catch((error) => card.log.error(error))32})33 34// Anything on the "run" input (a button, a schedule) starts a run35card.ports.onMessage(() => void runTests(), { input: 'run' })
This exact code passes strict type checking against the SDK, and the manifest passes the SDK’s own validator.

One connect()

It waits for the app’s handshake and returns a typed client: every method, event and payload is typed.

The manifest is the contract

Permissions, ports, tools and settings are declared up front — that is what the install dialog shows.

Test it without the app

createMockHost() runs the card against an in-memory host that applies the app’s own checks in the app’s order.

What the client can do

  • card.setStatus()Title, status, badge, overview tile, attention
  • card.uiToasts, confirmations and the card’s menu
  • card.storageStorage per card and per package
  • card.settingsA settings form the app draws, secrets included
  • card.agentsList, watch, read and prompt agents
  • card.terminalsRun commands in connected terminals
  • card.portsTyped inputs and outputs over arrows
  • card.toolsTools for connected agents
  • card.netHTTP through the app’s proxy, streaming too
  • card.fsFiles in the workspace folder
  • card.lifecycleVisibility, pausing, resizing
  • createTranslator()English, Russian and Chinese, switching live
Start

Your first card in three commands

You need Node.js 18.17 or newer, and NeuroSquad with developer mode turned on in Settings → Custom cards.

  1. 1

    Create it

    $npx @neurosquad/card-sdk create my-card

    A working starter: live agent statuses, a scratchpad that is saved, a port and a tool. Add --template react for React and Vite.

  2. 2

    Run it live

    $cd my-card && npx @neurosquad/card-sdk dev

    Links the folder to the app once you confirm, reloads the card on every save and streams its log.

  3. 3

    Check it

    $npx @neurosquad/card-sdk validate

    Runs the app’s own validators and prints the install dialog your users will see.

Then share it

Push the folder to GitHub. Anyone installs it by pasting owner/repo into Settings → Custom cards. Updates are never automatic: each new commit is shown with its permission changes and applied with a click.

Styling

Looks like whatever you want

The header, the frame and the menu are the app’s. Everything inside is yours: any HTML, CSS, canvas or 3D. An optional kit follows the app’s live theme if you want the native look.

Native kitThe optional classes, on the app’s own theme
TerminalMonospace and phosphor green
Your brandYour fonts, your colours, your layout
Questions

Before you start

Build the card your canvas is missing

Start from a template, run it live in the app, publish it on GitHub.

Windows 10 / 11 · 64-bit