Skilder
Skills & Hats

Writing Scripts

Use the Skilder Script SDK to call MCP tools, spawn sub-agents, and chain scripts together from TypeScript, Python, Bash, or JavaScript.

Skills can contain executable scripts — code that runs inside the Skilder runtime as part of a skill's capabilities. Scripts can call MCP tools, spawn sub-agents, and invoke other scripts, making them the most powerful building block in Skilder.


Supported Languages

LanguageRuntimeNotes
TypeScripttsxFull SDK support with @skilder-ai/runtime imports
JavaScriptnodeFull SDK support with @skilder-ai/runtime imports
Pythonpython3Runs as standalone script
BashbashRuns as standalone script

TypeScript and JavaScript scripts get access to the Script SDK — a set of functions for calling tools, delegating to sub-agents, and executing other scripts.


Script SDK

Install or import from @skilder-ai/runtime:

import {
  callTool, callToolText, callToolJson,
  delegate, delegateText, delegateJson,
  execute, executeText, executeJson,
  output,
} from '@skilder-ai/runtime';

Returning Structured Data

Use output(value) to emit the script's return value. The runtime captures each output() call on a dedicated channel, separate from console.log debug output.

import { output } from '@skilder-ai/runtime';

// Return a plain string
output('Done — entity stored');

// Return a structured object (will be JSON-serialized automatically)
output({ status: 'ok', rowsProcessed: 42 });

// Return the result of a tool call
const data = await callToolJson<MyData>('fetch_records', { limit: 100 });
output(data);

console.log is for debugging only. Its output is not included in the script's result and will not be seen by calling agents or executeJson. Use output() when you want the caller to receive a value.

FunctionBehavior
output(value: string)Emits the string as-is
output(value: object | number | ...)JSON-serializes the value (falls back to String(value) on error)

Calling Tools

Call any MCP tool available in your skill by name:

// Get the full result (content array)
const result = await callTool('fetch', { url: 'https://example.com' });

// Get just the text content as a string
const html = await callToolText('fetch', { url: 'https://example.com' });

// Parse the text content as JSON
const data = await callToolJson<MyType>('search_nodes', { query: 'automation' });
FunctionReturnsThrows if
callTool(name, args)CallToolResultResult is not a valid tool response
callToolText(name, args)stringFirst content block is not text
callToolJson<T>(name, args)TContent is not text or not valid JSON

Delegating to Sub-Agents

Spawn a sub-agent that has access to all available tools. The sub-agent runs autonomously until it completes the task:

// Delegate a complex task
const result = await delegate('Analyze the sales data and create a summary report');

// Get just the text response
const summary = await delegateText('Summarize the key findings from this data');

// Get structured JSON back
const analysis = await delegateJson<Report>('Analyze this data and return JSON');

You can override the model and temperature:

const result = await delegateText(
  'Write a creative tagline for this product',
  {
    model: 'anthropic/claude-sonnet-4-5-20250514',
    temperature: 0.8,
  }
);
FunctionReturnsOptions
delegate(context, options?)CallToolResultmodel, temperature (0.0–2.0)
delegateText(context, options?)stringSame as above
delegateJson<T>(context, options?)TSame as above

Sub-agents can nest up to 3 levels deep to prevent infinite delegation chains.

Executing Other Scripts

Call another script within the same skill or across skills using skill path notation:

// Execute a script in the same skill
const output = await executeText('/My Skill/transform.py', ['--input', 'data.csv']);

// Get structured JSON from another script
const config = await executeJson<Config>('/My Skill/generate-config.ts');

// Execute with no arguments
const result = await execute('/My Skill/cleanup.sh');

Path format: /Skill Name/script-filename.ext

FunctionReturnsThrows if
execute(path, args?)CallToolResultResult is not a valid response
executeText(path, args?)stringFirst content block is not text
executeJson<T>(path, args?)TContent is not text or not valid JSON

Output

FunctionDescription
output(value)Emit a structured return value. Strings pass through as-is; all other values are JSON-serialized. Call once or multiple times — values are joined with \n in the result.

Types

interface CallToolResult {
  content: ToolContent[];
  isError?: boolean;
}

type ToolContent = TextContent | ImageContent;

interface TextContent {
  type: 'text';
  text: string;
}

interface ImageContent {
  type: 'image';
  data: string;      // base64-encoded
  mimeType: string;
}

Creating a Script in the Editor

  1. Open a skill in the Skill Editor.
  2. In the file tree (left panel), right-click Scripts > Add Script.
  3. Set a name and description (the description helps agents understand what it does).
  4. Select the language: Python, Bash, JavaScript, or TypeScript.
  5. Choose the execution target:
    • AGENT — Runs in the runtime connected to the agent.
    • EDGE — Runs on a specific edge runtime (useful for local access).
  6. Write your code in the editor.

Example: Web Scraper with Memory

A TypeScript script that fetches a URL, extracts key information, and stores it in a knowledge graph:

import { callTool, callToolText, output } from '@skilder-ai/runtime';

// Fetch a web page
const html = await callToolText('fetch', {
  url: 'https://example.com/about',
});

// Store findings in the knowledge graph
await callTool('create_entities', {
  entities: [
    {
      name: 'Example Corp',
      entityType: 'Company',
      observations: ['Founded in 2020', 'B2B SaaS platform'],
    },
  ],
});

// Emit the result — this is what calling agents or executeJson will receive
output({ status: 'done', url: 'https://example.com/about' });

// console.log is for debugging only — not included in the result
console.log('Done — entity stored in knowledge graph');

Example: Multi-Step Analysis with Delegation

A script that gathers data from multiple tools, then delegates the analysis to a sub-agent:

import { callToolText, delegateText, output } from '@skilder-ai/runtime';

// Gather data from different sources
const salesData = await callToolText('query_database', {
  sql: 'SELECT * FROM sales WHERE date > NOW() - INTERVAL 30 DAY',
});

const supportTickets = await callToolText('search_tickets', {
  query: 'priority:high created:last-30-days',
});

// Delegate the analysis to a sub-agent
const report = await delegateText(
  `Analyze these two datasets and produce an executive summary:

   Sales data: ${salesData}
   Support tickets: ${supportTickets}

   Focus on correlations between sales volume and support load.`
);

// Return the report as the script's structured result
output(report);

Limits

  • Timeout: 30 seconds per script execution (default).
  • Output size: 10 MB maximum.
  • Delegate depth: Maximum 3 levels of nested sub-agents.
  • File size: Scripts themselves have no size limit, but assets are capped at 10 MB.

Next Steps