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 trust them in project config. Do not use wildcard env
overrides 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 trust/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 defineTool() 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 { definePrimaryCommand, defineTool } from '@opensip-cli/core';
const primaryCommand = definePrimaryCommand({
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.'],
}),
});
export const tool = defineTool({
identity: { name: 'hello-tools' },
metadata: {
id: '<stableId-from-manifest>',
version: '0.1.0',
description: 'Project-local typed tool',
},
commandSpecs: [primaryCommand],
});
createTool() remains as a compatibility wrapper over defineTool(), but new
templates teach the explicit command-spec path directly. Neither path adds
hidden lifecycle hooks.
When the tool owns stable failure codes, attach an immutable catalog via
extensionPoints.errorCatalog (TOOL_CONTRACT_VERSION ≥ 1.1.0) and throw with
createToolError — do not pre-stringify caught values before
cli.reportFailure. See the full tool plugin guide
and the error and resiliency model.
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. Trust the project-local Tool
opensip tools create adds the new tool id to tools.trusted in
opensip-cli.config.yml:
tools:
trusted:
- hello-tools
Commit that config entry with the tool so teammates and CI load the same
intentional project-local Tool. OPENSIP_CLI_ALLOW_PROJECT_TOOLS remains
available as an override, but it is not the normal scaffold path.
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 trust override environment variables | Environment variables |
| Understand Tool architecture | The tool-plugin model |