Create your first Tool
A Tool plugin adds a whole subcommand to opensip-cli. Use this path when your work is not a fitness check, simulation scenario, or graph adapter.
This guide scaffolds a tracked project-local Tool under opensip-cli/tools/ using
opensip tools create. It is the fastest way to understand the contract before you
package a Tool for npm.
Project-local tools are executable code, deny-by-default, and **not
sandboxed** after you allowlist them. Do not use wildcard trust unless you trust
every project-local tool in the repo.
1. Scaffold the tool
Minimal JS (default smoke path)
Zero npm dependencies — ideal for a quick allowlist/run check:
opensip tools create hello-tools
This writes opensip-cli/tools/hello-tools/opensip-tool.manifest.json and
index.mjs.
Typed local package (ts-local)
For TypeScript authoring with createTool() from @opensip-cli/core:
opensip tools create hello-tools --template ts-local
This adds package.json, tsconfig.json, src/index.ts, tests, and a README.
The sidecar points at ./dist/index.js, so build before validate/run:
cd opensip-cli/tools/hello-tools
pnpm install
pnpm run build
pnpm test
2. Sidecar manifest shape
Generated manifests include identity and stableId (required by the real
sidecar validator):
{
"kind": "tool",
"id": "hello-tools",
"identity": { "name": "hello-tools" },
"stableId": "8f1e2d3c-4b5a-6789-0abc-def123456789",
"name": "hello-tools",
"version": "0.1.0",
"apiVersion": 1,
"main": "./index.mjs",
"commands": [
{ "name": "hello-tools", "description": "Run hello-tools" }
]
}
The manifest is read before the module is imported. Runtime metadata.id must
match stableId; metadata.name and command names must match the manifest.
3. Runtime entry
minimal-js emits a dependency-free plain object. ts-local emits:
import { createTool } from '@opensip-cli/core';
export const tool = createTool({
identity: { name: 'hello-tools' },
metadata: {
id: '<stableId-from-manifest>',
version: '0.1.0',
description: 'Project-local typed tool',
},
primaryCommand: {
description: 'Run hello-tools',
commonFlags: ['json'],
scope: 'none',
output: 'command-result',
handler: async () => ({
type: 'text-lines',
title: 'hello-tools',
lines: ['Your project-local tool is ready.'],
}),
},
});
createTool() wraps defineTool() and does not add hidden lifecycle hooks.
Advanced tools can still use defineTool() directly.
4. Validate
# minimal-js
opensip tools validate opensip-cli/tools/hello-tools
# ts-local (after build + install deps in the tool dir)
opensip tools validate opensip-cli/tools/hello-tools --install-deps
Validation executes candidate code in a child process. It is a coherence check,
not a security sandbox.
5. Allowlist the project-local Tool
export OPENSIP_CLI_ALLOW_PROJECT_TOOLS=hello-tools
Use a comma-separated list for more than one Tool. Avoid '*' unless you trust
every project-local tool in the repo.
6. Run it
opensip hello-tools
opensip hello-tools --json
Logging and failures in your handler
Log run start with the scope-backed logger (writes to .runtime/logs/ when configured):
cli.logger.info({ evt: 'hello-tools.run.start', module: 'hello-tools:cli' });
When the command cannot run (missing recipe, bad path), report a failure through the host:
await cli.reportFailure({
message: 'Recipe not found: example',
exitCode: 2,
jsonRequested: opts.json === true,
});
Uncaught ToolError subclasses are also rendered by the host — prefer reportFailure
when you want a custom message or structured log event.
7. See it in the Tool inventory
opensip tools list --project
The row should show the hello-tools id, project source, and hello-tools command.
8. What changes for publishable Tools
The tracked sidecar layout is ideal while authoring inside one repo. To distribute a Tool:
- Move the runtime into an npm package.
- Put the manifest under
package.json#opensipToolswithkind: "tool",identity, andstableId. - Export
toolfrom the package main. - Run
opensip tools validate <spec>. - Install with
opensip tools install <spec>.
A publishable npm scaffold is deferred until consumption-side verification and
trust enforcement mature (see ADR-0076).
tools validate and tools install execute the candidate package module as part of validation. Install scripts are blocked and runtime probing has a timeout, but this is still code execution with your user privileges.
Where to go next
| You want to ... | Go to |
|---|---|
| Learn the full Tool contract | Full Tool plugins |
| Read the command grammar | Command surface taxonomy |
| Manage installed Tools | tools command |
| See the allowlist environment variable | Environment variables |
| Understand Tool architecture | The tool-plugin model |