Connect+Documentation
AI plugins

SDK & aip CLI reference

The @arw/ai-plugin package — types, manifest/argument validation, the devRun local runner, and the aip command-line tool.

@arw/ai-plugin is the authoring package for Connect+ AI-sidebar plugins: the TypeScript types for the manifest and the capability api, the manifest and argument validators, a local dev runner, and the aip command-line tool. Its validators are the exact checks the desktop runs — a parity test keeps the two in lockstep, so "valid here" means "valid in the app".

New to plugins? Start with Create an AI plugin for the concepts; this page is the API reference.

Install

npm install --save-dev @arw/ai-plugin

Types

Import the types to get full typing on a tool and its api:

import type { Tool, PluginApi, PluginManifest } from '@arw/ai-plugin';

// Tool<Args> — an async (args, api) => value. Args is your parameter shape.
export const random_fact: Tool<{ maxLength?: number }> = async (args, api) => {
  const res = await api.fetch(`https://catfact.ninja/fact?max_length=${args.maxLength ?? 140}`);
  return (res.json() as { fact: string }).fact;
};
TypeWhat it is
Tool<Args>The signature of a tool export: (args: Args, api: PluginApi) => Promise<unknown>.
PluginApiThe capability object a tool receives — fetch(url, init?) and secret(name).
PluginManifestThe typed shape of plugin.json (id, name, version, entry, permissions, tools).

Validation

import { validateManifest, validateArgs } from '@arw/ai-plugin';
  • validateManifest(json) — the install-time check: id/entry rules, permission shapes (hostname allowlist, page level, UPPER_SNAKE secret names), and each tool's name + JSON-Schema parameters. Returns the parsed manifest or throws with a precise message. Run it in CI to catch a bad manifest before you publish.
  • validateArgs(schema, args) — validates a call's arguments against a tool's parameters schema, exactly as the runtime does before your tool runs.

Local testing — devRun

Run a tool against a mock api (allowlisted fetch, declared-only secret, argument validation) without launching the app:

import { devRun } from '@arw/ai-plugin';
import manifest from './my-plugin/plugin.json';
import * as mod from './my-plugin/index.js';

const out = await devRun(mod, manifest, 'random_fact', { maxLength: 80 }, {
  // Optional. Omit `fetch` to use the real network (still host-allowlisted);
  // supply it to stub responses in a unit test.
  fetch: async (url) => ({ status: 200, headers: {}, body: '{"fact":"…"}' }),
  secrets: { API_TOKEN: 'test-value' },
});

devRun enforces the same guarantees as the desktop: a fetch to a host not in permissions.network throws, and api.secret(name) throws unless name is in permissions.secrets.

The aip CLI

aip validate ./my-plugin              # check plugin.json + the entry module
aip dev ./my-plugin random_fact n=1   # run a tool against the mock api
  • aip validate <dir> — runs validateManifest on the folder and confirms the entry module exports a function for every declared tool.
  • aip dev <dir> <tool> [key=value…] — invokes one tool. Arguments are key=value, each JSON-parsed (n=1 → number, on=true → boolean, s="hi" → string). Supply secrets with --secret NAME=value. Uses the real network, still restricted to the manifest's host allowlist.
aip dev ./my-plugin notion_save db=Tasks --secret NOTION_TOKEN=secret_xxx

Invoke without a global install via npx aip ….

Trust & publishing

A published plugin is a signed .aip package. Signed by the ARW root key → first-party; signed by your author key → signed. The desktop re-verifies the signature on every install and refuses a tampered, unsigned, or key-rotated package — the store is only distribution, never the trust root. See Create an AI plugin § Installing for the publish flow.

On this page