Skip to content

Writing a Skill

Skills extend OpenMotoko with new tools. This guide covers creating a skill from scratch, the manifest format, the defineSkill API, testing, and publishing.

Terminal window
npx create-openmotoko-skill my-skill
cd my-skill
pnpm install

This scaffolds a skill project with the manifest, entry point, and test setup.

my-skill/
manifest.json
src/
index.ts
test/
index.test.ts
package.json
tsconfig.json

Every skill needs a manifest.json:

{
"id": "my-skill",
"name": "My Skill",
"version": "1.0.0",
"description": "Does something useful",
"author": "your-name",
"capabilities": {
"network": false,
"filesystem": {
"enabled": false,
"paths": []
},
"shell": false,
"env": []
},
"tools": [
{
"name": "do_thing",
"description": "Does the thing with the input",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The input to process"
}
},
"required": ["query"]
}
}
]
}
FieldTypeDescription
networkbooleanOutbound HTTP access
filesystem.enabledbooleanFile read/write access
filesystem.pathsstring[]Allowed directories
shellbooleanShell command execution
envstring[]Environment variables the skill can read

Only declare what you actually need. The security scanner rejects skills that use undeclared capabilities.

import { defineSkill } from '@openmotoko/skill-sdk'
export default defineSkill(
require('./manifest.json'),
async (ctx, toolName, input) => {
switch (toolName) {
case 'do_thing':
const result = await processQuery(input.query)
return { content: result }
default:
throw new Error(`Unknown tool: ${toolName}`)
}
}
)

The ctx parameter provides:

PropertyTypeDescription
manifestSkillManifestThe parsed manifest
envRecord<string, string>Allowed environment variables
logLoggerStructured logger

Tool handlers return an object with a content field:

return { content: 'Result text here' }

For errors, throw an exception:

throw new Error('Something went wrong')

The SDK exports utilities:

FunctionDescription
formatToolResult(content)Wraps content in the expected result format
formatError(error)Formats an error for the IPC response
parseJsonInput(raw)Safely parses JSON input
validateInput(input, schema)Validates input against a JSON schema
import { SkillTestHarness } from '@openmotoko/skill-sdk'
import skill from '../src/index'
const harness = new SkillTestHarness(skill)
const result = await harness.runTool('do_thing', {
query: 'test input',
})
console.log(result.result)
console.log(result.logs)
console.log(result.durationMs)
FieldTypeDescription
toolNamestringTool that was called
inputobjectInput that was passed
resultobjectTool return value
logsstring[]Log output during execution
durationMsnumberExecution time

If you prefer not to use the CLI:

  1. Create a directory with manifest.json and src/index.ts
  2. Add @openmotoko/skill-sdk as a dependency
  3. Export a defineSkill call as the default export
  4. Write tests using SkillTestHarness
Terminal window
npx @openmotoko/cli publish ./my-skill

This uploads your skill to the OpenMotoko registry where it undergoes security scanning before being listed.

  • All capabilities are correctly declared in the manifest
  • No eval() or dynamic code execution
  • Environment variable usage matches capabilities.env
  • Tests pass via SkillTestHarness
  • Version follows semver

Users install published skills via the API:

Terminal window
curl -X POST http://localhost:3457/api/registry/install \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{"skillId": "my-skill"}'

Or through the web UI’s skill library.