Plugins Tool Authoring - HenriGrimm/Minnow GitHub Wiki
For new multi-tool packages, connections, panels and skills, use the Plugin API v1 guide. This page documents the compatible legacy single-tool format.
Minnow can load local tool plugins from ~/.minnow/tools/<pluginId>/ without running a separate MCP server. Each pack contains a JSON manifest and a single JavaScript handler executed on the Node tool server (npm start).
~/.minnow/tools/<pluginId>/
tool.json # metadata + OpenAI parameters schema
handler.mjs # export default async function handler(args, ctx)
The folder name must match tool.json β id. Plugin ids use lowercase letters, numbers, and hyphens (^[a-z0-9][a-z0-9-]*$).
The model sees:
plugin__<pluginId>__<functionName>
Hyphens in the plugin id become underscores in the namespace segment only. Example: id hello-world + function greet β plugin__hello_world__greet.
See documentation/schemas/tool-plugin.schema.json.
Required fields: id, functionName, label, description, category, parameters (JSON Schema object).
Optional capabilities (v1 metadata for future gating):
| Field | Default | Meaning |
|---|---|---|
filesystem |
false |
Declares filesystem access needs |
network |
false |
Declares network access needs |
nodeBuiltin |
false |
Reserved for trusted Node builtins |
/**
* @param {Record<string, unknown>} args β arguments from the model
* @param {import('../../server/tools/plugin-context.js').PluginContext} ctx
* @returns {Promise<string>} β result text (use "Error: β¦" for failures)
*/
export default async function handler(args, ctx) {
return `Hello, ${args.name ?? 'world'}`;
}ctx provides:
-
workspaceRootβ active workspace path -
minnowHomeβ~/.minnow(orMINNOW_HOME) -
log(message)β server log line -
envβ read-onlyNODE_ENV,MINNOW_HOME
Return a string from the handler. Uncaught errors are converted to Error: β¦ strings by the server.
~/.minnow/tools.json:
{
"plugins": {
"my-plugin": { "enabled": true }
},
"permissions": {
"default": {
"plugin__my_plugin__greet": "ask"
}
}
}-
plugins.<id>.enabled: falseremoves the tool fromGET /api/plugins/toolsand from chat tool lists. -
permissions.default.<namespacedName>usesfull,ask, oroff(default for new plugin tools isask).
Configure in Settings β Tools β Plugins, or edit tools.json directly.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/plugins/ping |
Health check |
| GET | /api/plugins |
List plugin metadata |
| GET | /api/plugins/tools |
OpenAI function definitions (enabled packs) |
| POST | /api/plugins/reload |
Rescan packs and clear handler cache |
| POST | /api/plugins/scaffold |
Copy _template to ~/.minnow/tools/<id>/
|
Execution uses the same path as built-in server tools: POST /api/tools with { "name": "plugin__β¦", "args": {} }.
With npm start running:
curl -X POST http://localhost:9473/api/plugins/scaffold \
-H 'Content-Type: application/json' \
-d '{"id":"demo"}'Or use Scaffold new pluginβ¦ in Settings β Tools β Plugins.
By default, handlers run in a Node vm wrapper with a minimal sandbox (args, ctx, limited console.log). For local development only, set MINNOW_PLUGIN_UNSAFE=1 to load handlers via full dynamic import() (same OS user as the dev server β only use on trusted code).
| MCP | Native plugin | |
|---|---|---|
| Process | Separate stdio server | In-process handler |
| Naming | mcp__server__tool |
plugin__id__function |
| Best for | Existing MCP tools | Quick local extensions |
npm run test:pluginsFixtures live under test/fixtures/plugin-tools/echo/ (plugin__echo__ping β pong).