Nihmbus Extensions — Author Guide
A Nihmbus Extension is an installable package that adds capabilities to the agent. It's the equivalent of an OpenClaw plugin, and it maps onto three concepts Nihmbus already has:
| Concept | What it is | Where it lives |
|---|---|---|
| Skill | A SKILL.md markdown playbook that orchestrates tools. ClawHub / Anthropic-Agent-Skills compatible. |
a skills row |
| Tool | An executable capability the agent can call. | the agent tool registry |
| Extension | A package bundling skills and/or tools (via a remote MCP server). | extension_installations |
A plain ClawHub skill (just a SKILL.md) is the degenerate case: an extension
with skills and no MCP server. Conversely, a full extension can ship both a
playbook and the executable tools that playbook calls.
As of extension API 1.1.0, an extension can also contribute product surface — a UI panel + nav, named callable actions, its own HTTP endpoints, and its own workspace-scoped data:
| Block | Adds | See |
|---|---|---|
ui |
A client-side React panel + sidebar nav (/x/<ext>) |
contribution-points §2 |
actions |
Named actions ext.<ext>.<name> (via /api/invoke, optionally agent tools) |
§3 |
api |
An HTTP namespace /api/ext/<ext>/* + the built-in /data store |
§3 |
This guide covers skills + the MCP tool tier; /help/extensions/contribution-points is the deep reference for ui/actions/api/data and the security model.
Executable tools are MCP-only. Nihmbus runs serverless, so extensions contribute executable agent tools by declaring a remote MCP server (Streamable-HTTP or SSE). Nihmbus connects as an MCP client per agent request and merges the server's tools into the registry, namespaced
ext__<name>__<tool>. (UI runs client-side via Module Federation;actions/apirun server-side under the calling user's context — see the contribution-points doc.)
Package layout
my-extension/
nihmbus.extension.json # the manifest (this file's format below)
skills/
my-skill/
SKILL.md # one or more bundled skills
The manifest — nihmbus.extension.json
The authoritative schema is extensionManifestSchema in
lib/extensions/manifest.ts (re-exported from
@/lib/extensions). Validate your manifest against it:
import { extensionManifestSchema } from '@/lib/extensions'
extensionManifestSchema.parse(JSON.parse(fs.readFileSync('nihmbus.extension.json', 'utf8'))){
"manifestVersion": 1,
"name": "vercel", // lowercase slug, unique per workspace
"displayName": "Vercel / v0",
"version": "0.1.0", // semver of YOUR extension
"publisher": "nihmbus",
"description": "Generate a landing page with v0, buy a domain, deploy …",
"homepage": "https://vercel.com",
"icon": "▲", // emoji or URL
"engines": { "nihmbus": ">=1.0.0" },// checked against the extension API version
"permissions": ["spend", "domains.buy", "deploy"], // ENFORCED (see "Permissions" below)
"secrets": [
{
"key": "VERCEL_TOKEN", // referenced by mcp.authSecret
"label": "Vercel access token",
"description": "Token with v0, Domains, and Deployments scope.",
"required": false, // false ⇒ tools may run in a simulated/limited mode
"source": "vault", // 'vault' (encrypted, default) | 'env'
"category": "integrations"
}
],
"mcp": {
"transport": "streamable-http", // 'streamable-http' | 'sse'
"url": "https://mcp.example.com/mcp", // may use {BASE_URL} for in-repo servers
"authSecret": "VERCEL_TOKEN", // which declared secret provides auth
"headers": { "Authorization": "Bearer {secret}" }, // {secret} / {secret.FIELD} interpolated
"toolAllowlist": ["generate_landing_page", "buy_domain", "deploy"], // optional
"mutatingTools": ["buy_domain", "deploy"] // see "Spend & approval" below
},
// Optional product-surface blocks (extension API 1.1.0). Full reference:
// docs//help/extensions/contribution-points
"ui": {
"remoteName": "ext_vercel",
"remoteEntry": "/ext-remotes/vercel/remoteEntry.js", // https URL, version-pinned
"exposedModule": "./Panel",
"requestedIsolation": "federated", // a request; the HOST decides the tier
"nav": [{ "id": "home", "label": "Vercel / v0", "icon": "▲", "route": "" }]
},
"actions": [{
"name": "deploy", // → ext.vercel.deploy
"description": "Deploy and attach a domain.",
"method": "POST", "mutating": true,
"inputSchema": { "type": "object", "properties": { "projectName": { "type": "string" } }, "required": ["projectName"] },
"handler": { "kind": "mcp", "tool": "deploy" }, // or { "kind": "http", "path": "/deploy" }
"requiredPermissions": ["deploy"]
}],
"api": {
"routes": [{ "method": "GET", "path": "/summary", "requiredPermissions": [] }]
},
"skills": [{ "path": "skills/launch-event-landing-page" }]
}A skills-only manifest omits mcp and secrets entirely — that's a valid
extension and the same shape a bare ClawHub skill synthesizes into on import.
The ui/actions/api blocks are likewise optional and independent.
SKILL.md — the playbook
Standard Agent Skills / ClawHub format: YAML
frontmatter + markdown body. Only the description (and triggers) are injected
into the agent's prompt; the full body loads on demand via skill_load.
---
name: launch-event-landing-page
description: Spin up a promotable landing page … Use when the user says "make a landing page and market it".
license: MIT
triggers: [landing page, v0, launch site]
---
# Launch event landing page
## Procedure
1. Resolve the subject …
2. Call `ext__vercel__generate_landing_page` …
3. **Checkpoint (spend):** confirm, then call `ext__vercel__buy_domain` …
4. Deploy and market with `publish_to_channels`.Recognized frontmatter: name, description (required); license, homepage,
triggers, user-invocable, disable-model-invocation, command-dispatch,
command-tool. Unknown keys are preserved verbatim in the skill's frontmatter.
The MCP server contract
Your server is a standard MCP server reachable over Streamable-HTTP. Nihmbus:
- Resolves
mcp.authSecretand interpolates it intomcp.headers(so your server authenticates the request however you like — typically a bearer token). - Calls
listTools()and registers each tool asext__<name>__<tool>. - Calls
callTool({ name, arguments })when the agent invokes one. Return the standard MCP result ({ content: [{ type: 'text', text }] }, orstructuredContent); setisError: trueto surface a failure to the agent.
See app/api/ext/vercel-mcp/[transport]/route.ts for a
working reference server built with mcp-handler.
It runs in simulated mode when no token is configured — a good pattern so your
extension demos on a fresh install.
In-repo vs vendor-hosted MCP servers. The bundled Vercel and Commerce extensions point
mcp.urlat our own in-repo servers ({BASE_URL}/api/ext/…), which make REST calls to the vendor and add guardrails (pricing band, idempotency, simulated fallback, a curated tool surface). To point an extension at a vendor's own hosted MCP server instead, you only change themcpblock — see/help/extensions/swapping-mcp-serversfor the how + the trade-offs.
Installing
From the Extensions page (/extensions) or extensions.install:
- catalog — bundled, one-click (e.g. the Vercel reference extension).
- git / clawhub —
owner/repo[/subdir][@ref]. A repo with anihmbus.extension.jsoninstalls as a full extension; a repo with only aSKILL.mdsynthesizes a skill-only extension (full ClawHub interop). - url — a JSON manifest, or
{ manifest, skills }. - local — a package dir under
extensions/.
Installing creates the skills rows (published, so they're discoverable) and registers the MCP server. Uninstalling removes the row and cascade-deletes its skills.
Permissions (enforced)
permissions is no longer consent-only. The subset granted at install is stored
on the install (granted_permissions), and an action, api route, or data op
that declares requiredPermissions is rejected (403) unless the install holds
them. data.read / data.write gate the built-in store. See
contribution-points §4.
Spend & approval (important)
MCP tools are opaque — Nihmbus can't tell that buy_domain spends money. Guard
spend with all three:
mcp.mutatingTools— tags the tool with a "mutates state" hint in its description (the same signal native mutating actions carry).permissions— granted at install and enforced foractions/api.- The skill body — make spend steps an explicit confirmation checkpoint
("only after the user confirms, call
buy_domain").
Limitation:
requiresRoleand the supervised-mission mutation queue apply to native + extensionactions— they do not gate raw MCP tool calls invoked inside the model loop. Hard spend caps must live in your MCP server.
Security notes
- MCP /
api.baseUrlURLs must behttps://(localhost allowed for dev); private/link-local and cloud-metadata hosts are blocked (SSRF guard). - Secrets are encrypted at rest (AES-256-GCM) in the vault, flow only into outbound headers (never logged), and each resolution is audited.
- UI is a trust decision, not a sandbox. The in-DOM
federatedtier runs with the user's ambient authority and is reserved forpublisher: 'nihmbus'+ allowlisted publishers (EXT_TRUSTED_PUBLISHERS); everyone else is forced to a sandboxediframe. See contribution-points §4.
Roadmap
- Declarative-REST and bundled-JS executable-tool tiers (MCP is the only tier today).
- A
postMessagehost-SDK bridge for theiframeUI tier. - A hosted Nihmbus extension marketplace (today: bundled catalog + git/ClawHub/local).