Manifest reference
Every card has a neurosquad-card.json at the root of its folder. The app reads it at install and
refuses a card whose manifest does not pass; npx @neurosquad/card-sdk validate runs the very same
checks on your machine.
For editor completion, point $schema at the schema that ships with the SDK:
./node_modules/@neurosquad/card-sdk/schema/neurosquad-card.v1.json (React template) or
./vendor/neurosquad-card.schema.json (plain template). It is also exported as
@neurosquad/card-sdk/schema.json.
A complete example
{
"$schema": "./node_modules/@neurosquad/card-sdk/schema/neurosquad-card.v1.json",
"manifestVersion": 1,
"name": "test-radar",
"displayName": { "en": "Test radar", "ru": "Радар тестов", "zh": "测试雷达" },
"version": "1.0.0",
"protocol": 1,
"description": "Runs the test suite in a connected terminal and shows what broke.",
"author": { "name": "Acme", "url": "https://acme.dev" },
"license": "MIT",
"homepage": "https://acme.dev/test-radar",
"keywords": ["tests", "ci"],
"icon": "icon.png",
"minAppVersion": "0.1.100",
"entry": "dist/index.html",
"card": {
"defaultSize": { "w": 460, "h": 340 },
"minSize": { "w": 320, "h": 220 }
},
"permissions": [
"agents.read",
"terminals.write",
{ "id": "fs.read", "reason": "Reads the JUnit report" },
{ "id": "network", "hosts": ["api.github.com"], "reason": "Links failures to GitHub issues" },
{ "id": "clipboard.write", "optional": true }
],
"settings": [
{ "key": "command", "type": "string", "label": "Test command", "default": "npm test" },
{ "key": "githubToken", "type": "secret", "label": "GitHub token", "scope": "package" }
],
"ports": {
"inputs": [{ "id": "run", "label": "Run", "type": "ns:trigger", "default": true }],
"outputs": [
{ "id": "failures", "label": "Failures", "type": "ns:tasks", "retain": true },
{ "id": "summary", "label": "Summary", "type": "ns:markdown" }
]
},
"tools": [
{
"name": "run_tests",
"title": "Run the tests",
"description": "Runs the project's tests and returns failing test names with messages.",
"inputSchema": {
"type": "object",
"properties": { "filter": { "type": "string", "maxLength": 200 } }
},
"timeoutMs": 120000
}
]
}Identity
| Field | Required | Rule | Meaning |
|---|---|---|---|
$schema | string | For your editor. Ignored by the app. | |
manifestVersion | yes | 1 | The manifest format. |
name | yes | a-z, digits and -, starts with a letter, 2–48 chars | The package slug. It prefixes the card’s tool names and namespaces its custom port types. Reserved (built-in cards and tool families): neurosquad, neurosquad-card, custom, canvas, browser, terminal, agent, note, todo, kanban, host, system, telegram, image, reference, ports, web-watch, watch, timer, sticky, standup, squad, mcp, skill, official. |
displayName | yes | localized text, ≤ 80 | The card’s name in menus, the header and the install dialog. |
version | yes | SemVer (1.2.0, 1.2.0-beta.1) | Informational — installs are pinned to a commit, not a version. Bump it anyway: users see it on update. |
protocol | yes | integer | The card protocol you built against — 1 today. A card built for a newer protocol than the app speaks is refused with “needs a newer NeuroSquad”. |
description | localized text, ≤ 300 | Shown in the install dialog and the card picker. | |
author | { "name", "url"? } | url must be https://. Shown as self-declared. | |
license | string ≤ 100 | An SPDX id such as MIT. | |
homepage | https:// URL | ||
keywords | ≤ 20 unique strings, ≤ 40 chars each | ||
icon | path to a .png or .webp in the package | Square, ≤ 128 KB (128×128 or larger looks sharp). Checked by its bytes at install. Without one the card shows a puzzle piece. | |
minAppVersion | SemVer | The lowest NeuroSquad version that can run the card. | |
entry | path to an .html file, default index.html | The page loaded into the card. |
Localized text is either a plain string or an object with English required:
{ "en": "Test radar", "ru": "Радар тестов", "zh": "测试雷达" }. The app shows the user’s language
and falls back to English.
Text rules. Names, descriptions, labels, reasons and tool descriptions may not contain control
characters (tab and newline are allowed in descriptions), bidi overrides or isolates, line
separators, zero-width or other invisible characters. Only official cards (published from the
glmn-ai organization) may call themselves “NeuroSquad”, “official” or “verified” in displayName
or author — validate warns, the app refuses.
Paths (entry, icon) are relative to the manifest, with / separators — no .., no leading
/, no backslashes, no Windows device names (con, nul…), nothing under __ns/ (the app reserves
it), at most 20 levels and 240 characters.
Size — card
"card": {
"defaultSize": { "w": 460, "h": 340 },
"minSize": { "w": 320, "h": 220 },
"maxSize": { "w": 1200, "h": 900 }
}Sizes are in canvas units (CSS pixels at 100 % zoom), whole numbers, width 200–2400 and height
120–1800, with minSize ≤ defaultSize ≤ maxSize. Defaults: 420×320, min 200×120, max 2400×1800.
The user resizes within min/max; card.requestResize() is clamped
to them too.
Permissions — permissions
A list (≤ 32) of permission ids, or objects that add details:
"permissions": [
"agents.read",
{ "id": "network", "hosts": ["api.github.com", "*.example.com", "status.example.org:8443"] },
{ "id": "fs.read", "reason": { "en": "Reads the JUnit report", "ru": "Читает отчёт JUnit" } },
{ "id": "clipboard.write", "optional": true }
]| Key | Meaning |
|---|---|
id | One of the ids on Permissions. |
hosts | network only, and required there: ≤ 32 host patterns. |
reason | Localized text ≤ 300, shown under the permission in the install dialog. Say why. |
optional | true = not asked at install; the card asks at runtime with card.permissions.request(). |
Required permissions are all-or-nothing at install. Listing the same id twice merges it (and a required entry wins over an optional one).
Host patterns for network: lowercase host names only — api.example.com,
api.example.com:8443 (a port other than 443), or *.example.com (any subdomain, not
example.com itself). No scheme, path, IP address, bare * or localhost — this computer is the
separate network.local permission.
Settings form — settings
Up to 40 fields. The app draws the form (open it from the card’s ⋯ → Settings… or with
card.settings.open()); the card reads the values.
"settings": [
{ "key": "command", "type": "string", "label": "Test command", "default": "npm test", "maxLength": 200 },
{ "key": "notes", "type": "text", "label": "Notes", "placeholder": "Anything the agents should know" },
{ "key": "interval", "type": "number", "label": "Check every (min)", "default": 5, "min": 1, "max": 60, "step": 1 },
{ "key": "compact", "type": "boolean", "label": "Compact view", "default": false },
{ "key": "branch", "type": "select", "label": "Branch", "default": "main",
"options": [{ "value": "main", "label": "main" }, { "value": "dev", "label": "dev" }] },
{ "key": "accent", "type": "color", "label": "Accent", "default": "#22c55e" },
{ "key": "apiKey", "type": "secret", "label": "API key", "required": true, "scope": "package" }
]| Key | Meaning |
|---|---|
key | Starts with a letter; letters, digits, _, -; ≤ 64. Unique. |
type | string (one line), text (several lines), number, boolean, select, color (#rrggbb), secret. |
label, description, placeholder | Localized text (≤ 80, ≤ 300, ≤ 80). |
default | Of the field’s type. Must be one of the options for select, #rrggbb for color, within min/max for number. Not allowed for secret. |
required | The form will not save without it. |
scope | instance (default): per card on the canvas. package: shared by every card of this package. |
maxLength | string, text, secret. Defaults: 2 000 for string, 20 000 for text. |
min, max, step | number. |
options | select only, 1–50 { "value", "label" }. |
Secrets are entered only in the app’s form, stored encrypted, and never reach the card: the card
only learns whether one is set, and uses it through a {{secret:<key>}} placeholder in
network request headers.
Ports — ports
Typed data connections over arrows: up to 16 inputs and 16 outputs. Full guide:
Ports.
"ports": {
"inputs": [
{ "id": "run", "label": "Run", "type": "ns:trigger", "default": true },
{ "id": "lookup", "label": "Look up", "type": "ns:text", "mode": "request",
"response": { "type": "ns:json" } }
],
"outputs": [
{ "id": "failures", "label": "Failures", "type": "ns:tasks", "retain": true },
{ "id": "coverage", "label": "Coverage", "type": "test-radar/coverage",
"description": "Line coverage per file, 0..1",
"schema": {
"type": "object",
"additionalProperties": { "type": "number", "minimum": 0, "maximum": 1 }
} }
]
}| Key | Applies to | Meaning |
|---|---|---|
id | both | a-z, digits, -, starts with a letter, ≤ 32. Unique per direction. |
label, description | both | Localized text (≤ 80, ≤ 500). Shown to users, peer cards and agents discovering the port. |
type | both | A well-known ns:* type or a custom <package-name>/<type-name>. |
schema | both | Extra JSON Schema the value must also match. Required for custom types. |
mode | inputs | stream (default): fire-and-forget messages. request: the sender waits for a reply. |
response | request inputs | { "type", "schema"? } — the reply’s type. Required for mode: "request". |
default | inputs | The preferred input when a peer’s output fits several. At most one. |
retain | outputs | The app keeps the last value: peers can read it at any time, and a newly connected peer gets it once. |
A custom type from another package’s namespace is allowed (with a warning) — that is how two cards agree on a shared format.
Tools — tools
Up to 32 tools that agents connected to the card can call. Guide: Tools for agents.
| Key | Meaning |
|---|---|
name | a-z, digits, _, starts with a letter, ≤ 40. The agent sees <name with - → _>_<tool>, e.g. test_radar_run_tests. That exposed name may not equal a built-in NeuroSquad tool (canvas_spawn_card, terminal_send_keys…) and may not contain __. |
title | ≤ 80, a human title. |
description | ≤ 2 000, for the model, in English: what it does and what it returns. |
inputSchema | JSON Schema with "type": "object". The property card is reserved (the app adds it). |
readOnly | true when the tool changes nothing — a hint shown to the agent. |
timeoutMs | 1 000–120 000, default 30 000. |
JSON Schemas you write
Port schemas, response schemas and tool inputSchema use a safe subset of JSON Schema 2020-12,
checked at install (≤ 500 nodes, ≤ 16 levels deep):
- Supported:
type,enum,const,properties,required,additionalProperties,minProperties,maxProperties,items,minItems,maxItems,uniqueItems,minLength,maxLength,format,minimum,maximum,exclusiveMinimum,exclusiveMaximum,multipleOf,anyOf,oneOf,$ref(#or#/$defs/<name>),$defs. - Formats:
uri,date-time,date,email,color,uuid. - Ignored annotations:
$id,$schema,$comment,title,description,default,examples,deprecated,readOnly,writeOnly. - Caps:
enum≤ 256 options and ≤ 16 KB in total;const≤ 4 KB. - Not allowed:
pattern(regular expressions from a card would run in the app — a denial of service risk), and any keyword not listed above.
The schema’s $id, https://neurosquad.ai/schemas/neurosquad-card.v1.json, is an identifier, not
a download: point $schema at the copy in the SDK.