Manifest Reference
NimbleBrain reads host metadata from the _meta["ai.nimblebrain/host"] key in your MCPB manifest.json. This is where you declare your app’s name, icon, placements (sidebar items, main views, and settings sections), and briefing facets.
Full manifest structure
Section titled “Full manifest structure”{ "manifest_version": "0.4", "name": "@myorg/my-app", "version": "1.0.0", "description": "My NimbleBrain app", "author": { "name": "My Org", "email": "dev@myorg.com", "url": "https://myorg.com" }, "server": { "type": "python", "mcp_config": { "command": "python", "args": ["-m", "my_app.server"] } }, "_meta": { "ai.nimblebrain/host": { "host_version": "1.0", "name": "My App", "icon": "database", "placements": [ { "slot": "main", "resourceUri": "ui://dashboard", "priority": 50, "label": "My App", "icon": "database", "route": "my-app", "size": "full" }, { "slot": "settings", "resourceUri": "ui://settings", "label": "My App", "icon": "settings" } ] } }}MCPB base fields
Section titled “MCPB base fields”These fields are defined by the MCPB specification. NimbleBrain supports both v0.3 and v0.4 formats.
| Field | Type | Required | Description |
|---|---|---|---|
manifest_version | string | No | MCPB spec version ("0.3" or "0.4"). |
name | string | Yes | Scoped package name (e.g., @myorg/my-app). |
version | string | Yes | Semver version string. |
description | string | No | Human-readable description. |
author | object | No | Author info: name, email, url. |
server.type | string | Yes | Runtime type: "python", "node", "binary", or "uv". |
server.entry_point | string | No | Entry point file path. |
server.mcp_config | McpConfig | Yes | Spawn configuration (see below). |
_meta | object | No | Extension metadata. NimbleBrain reads _meta["ai.nimblebrain/host"]. |
McpConfig
Section titled “McpConfig”| Field | Type | Required | Description |
|---|---|---|---|
command | string | Yes | Executable to run ("python", "node", etc.). |
args | string[] | No | Command-line arguments. Use ${__dirname} for the bundle directory. |
env | Record<string, string> | No | Environment variables passed to the process. |
Host metadata: _meta["ai.nimblebrain/host"]
Section titled “Host metadata: _meta["ai.nimblebrain/host"]”| Field | Type | Required | Description |
|---|---|---|---|
host_version | string | Yes | Host extension contract version: "1.0" or "1.1". 1.1 is the version that adds the host_capabilities block — set it when you declare that block. |
name | string | Recommended | Display name shown in the sidebar and app list. Without a name the host surfaces no UI for the bundle, so it’s required in practice for any app with a presence in the shell. |
icon | string | No | Lucide icon name in kebab-case (e.g., "database", "message-square", "hand"). Defaults to "". |
category | string | No | Reserved, free-form string. Not currently consumed by the host — declare it only as forward-looking metadata. |
placements | PlacementDeclaration[] | No | Shell layout placement declarations — sidebar items, main views, and settings sections. See Placements. |
briefing | object | No | Daily briefing contribution. See briefing below. |
host_capabilities | object | No | NimbleBrain host capabilities this bundle requires or prefers. Declare host_version: "1.1" alongside it. See Host Capabilities. |
Settings sections
Section titled “Settings sections”A settings section is a placement with slot: "settings", not a separate top-level object. Add an entry to placements and the host renders it as a tab under Settings → This Workspace → Apps → <your app>, served from the placement’s resourceUri:
{ "slot": "settings", "resourceUri": "ui://settings", "label": "My App", "icon": "settings"}The resourceUri points at the ui:// page that renders the settings HTML; label and icon set the tab’s appearance. See Placements for the full PlacementDeclaration shape.
briefing object
Section titled “briefing object”Apps can contribute to the daily briefing on the Home dashboard by declaring a briefing block. The platform reads entity data from disk and presents a business-language summary.
| Field | Type | Required | Description |
|---|---|---|---|
briefing.priority | "high" | "medium" | "low" | No | Relative ordering in the briefing. High-priority apps appear first. Default: "medium". |
briefing.facets | BriefingFacet[] | Yes | Summary dimensions this app contributes. |
BriefingFacet
Section titled “BriefingFacet”Each facet describes one piece of data to surface in the briefing:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique identifier (kebab-case). |
label | string | Yes | Human-readable label (e.g., “Pipeline”, “Blocked tasks”). |
type | string | Yes | How this facet is treated: "attention" (needs action now), "upcoming" (coming soon), "activity" (something happened), "delta" (state changed), "kpi" (headline number). |
entity | string | No | Upjack entity to query. Reads JSON files from the entity data directory. |
query | object | No | MongoDB-style filter (powered by sift.js). Supports $eq, $ne, $gt, $gte, $lt, $lte, $in, $exists. String values like "${today}" are resolved at runtime. |
tool | string | No | MCP tool to call at briefing time (e.g., "newsapi__search_news"). |
tool_input | object | No | Arguments passed to the tool. |
resource | string | No | briefing:// URI — the app computes its own summary via MCP resource. |
metric | "count" | "sum" | "list" | No | Aggregation hint. |
description | string | No | Fallback description if data can’t be resolved. |
Each facet resolves via one of entity, tool, or resource.
Query variables resolved at runtime:
| Variable | Value |
|---|---|
${today} | Current date as YYYY-MM-DD |
${now} | Current ISO timestamp |
${period.since} | Start of the briefing period (24 hours ago) |
Example:
"briefing": { "priority": "medium", "facets": [ { "name": "due_today", "label": "Due today", "type": "upcoming", "entity": "task", "query": { "due_date": "${today}", "completed_at": null }, "metric": "count", "description": "Tasks due today that aren't done" }, { "name": "blocked_tasks", "label": "Blocked", "type": "attention", "entity": "task", "query": { "column": "blocked", "status": "active" }, "metric": "count", "description": "Tasks stuck in blocked column" } ]}PlacementDeclaration
Section titled “PlacementDeclaration”Each entry in the placements array:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
slot | string | Yes | — | Shell slot to fill: "sidebar", "sidebar.apps", "sidebar.bottom", "main", or "settings". See Placements. |
resourceUri | string | Yes | — | ui:// URI served by this MCP server. |
priority | number | No | 100 | Sort order within the slot. Lower values appear first. |
label | string | No | — | Human-readable label for sidebar items and tabs. |
icon | string | No | — | Lucide icon name (kebab-case). Falls back to CircleDot if unset or unrecognized. |
route | string | No | — | Route path for "main" slot placements. Registers as /app/<route>. |
size | "compact" | "full" | "auto" | No | — | Size hint for the slot renderer. |
Registering your main view
Section titled “Registering your main view”placements is what the shell renders and routes from. To give your app a navigable main view, declare a "main" slot placement with a route:
"placements": [ { "slot": "main", "resourceUri": "ui://dashboard", "route": "my-app", "label": "My App", "icon": "database" }]The virtual primary resource path resolves to the first declared placement’s resourceUri, so the shell can load your main view without knowing its URI ahead of time. See UI Resources for how primary resolves.
The schema also accepts a primaryView object for back-compat, but the host does not consume it — placements are the source of truth for what is registered and routed. Declare a "main" placement, not primaryView.
Icons use Lucide names. The resolver accepts both kebab-case and PascalCase:
"message-square"resolves to theMessageSquarecomponent"database"resolves toDatabase"hand"resolves toHand
If the icon name is not recognized, CircleDot is used as the default fallback.
Minimal example (UI app)
Section titled “Minimal example (UI app)”The smallest manifest that registers a UI view:
{ "name": "@myorg/hello", "version": "0.1.0", "server": { "type": "node", "mcp_config": { "command": "node", "args": ["${__dirname}/dist/index.js"] } }, "_meta": { "ai.nimblebrain/host": { "host_version": "1.0", "name": "Hello", "icon": "hand", "placements": [ { "slot": "main", "resourceUri": "ui://index", "route": "hello", "label": "Hello" } ] } }}Minimal example (tools only)
Section titled “Minimal example (tools only)”No _meta needed for tools-only bundles:
{ "name": "@myorg/calculator", "version": "1.0.0", "server": { "type": "python", "mcp_config": { "command": "python", "args": ["-m", "calculator.server"] } }}