Basic Tool
Create a tool with thetool() factory function:
Copy
import { tool, serve } from '@reminix/runtime';
const getWeather = tool('get_weather', {
description: 'Get the current weather for a city',
input: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' },
units: { type: 'string', default: 'celsius' },
},
required: ['location'],
},
handler: async (input) => {
const location = input.location as string;
const units = input.units as string || 'celsius';
return {
location,
temperature: 22,
units,
condition: 'sunny',
};
},
});
serve({ tools: [getWeather], port: 8080 });
Tool Options
Thetool() function takes a name and options object:
Copy
const myTool = tool('tool_name', {
// Required: Human-readable description
description: 'What this tool does',
// Required: JSON Schema for input
input: {
type: 'object',
properties: {
param1: { type: 'string', description: 'First parameter' },
param2: { type: 'number', default: 10 },
},
required: ['param1'],
},
// Optional: JSON Schema for output (for documentation)
output: {
type: 'object',
properties: {
result: { type: 'string' },
},
},
// Required: Handler function
handler: async (input, context) => {
return { result: 'done' };
},
});
Parameter Schema
Define input using JSON Schema:Copy
const processTool = tool('process', {
description: 'Process data with various types',
input: {
type: 'object',
properties: {
// String parameter
text: { type: 'string', description: 'Input text' },
// Number parameter
count: { type: 'integer', description: 'Item count' },
// Boolean parameter
enabled: { type: 'boolean', default: true },
// Array parameter
tags: {
type: 'array',
items: { type: 'string' },
description: 'List of tags',
},
// Object parameter
options: {
type: 'object',
properties: {
format: { type: 'string' },
},
},
// Enum parameter
mode: {
type: 'string',
enum: ['fast', 'accurate', 'balanced'],
default: 'balanced',
},
},
required: ['text', 'count'],
},
handler: async (input) => {
return { processed: true };
},
});
Handler Function
The handler function receives input and optional context:Copy
const myTool = tool('my_tool', {
description: 'Example tool',
input: {
type: 'object',
properties: {
query: { type: 'string' },
},
required: ['query'],
},
handler: async (input, context) => {
// input: Record<string, unknown> - the tool input
const query = input.query as string;
// context: Record<string, unknown> | undefined - optional metadata
const userId = context?.userId as string | undefined;
// Return any serializable value
return { query, userId };
},
});
Sync and Async Execution
Both sync and async handlers are supported:Copy
// Async handler
const fetchTool = tool('fetch_data', {
description: 'Fetch data from a URL',
input: {
type: 'object',
properties: {
url: { type: 'string' },
},
required: ['url'],
},
handler: async (input) => {
const response = await fetch(input.url as string);
return await response.json();
},
});
// Sync handler
const calculateTool = tool('calculate', {
description: 'Perform calculations',
input: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
},
required: ['a', 'b'],
},
handler: (input) => {
const a = input.a as number;
const b = input.b as number;
return { sum: a + b, product: a * b };
},
});
Error Handling
Return errors as part of the output or throw exceptions:Copy
const divideTool = tool('divide', {
description: 'Divide two numbers',
input: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
},
required: ['a', 'b'],
},
handler: (input) => {
const a = input.a as number;
const b = input.b as number;
if (b === 0) {
// Option 1: Return error in output
return { error: 'Cannot divide by zero' };
}
return { result: a / b };
},
});
const validateTool = tool('validate', {
description: 'Validate data',
input: {
type: 'object',
properties: {
data: { type: 'object' },
},
required: ['data'],
},
handler: (input) => {
const data = input.data as Record<string, unknown>;
if (!data.requiredField) {
// Option 2: Throw exception (caught and returned as error)
throw new Error('Missing required field');
}
return { valid: true };
},
});
error field:
Copy
{
"output": null,
"error": "Missing required field"
}
Output Schema
Define an output schema for documentation:Copy
const getUserTool = tool('get_user', {
description: 'Get user information',
input: {
type: 'object',
properties: {
userId: { type: 'string' },
},
required: ['userId'],
},
output: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
handler: async (input) => {
return {
id: input.userId as string,
name: 'John Doe',
email: '[email protected]',
};
},
});
/info.
Serving Tools
Serve one or more tools:Copy
import { tool, serve } from '@reminix/runtime';
const toolA = tool('tool_a', {
description: 'Tool A',
input: { type: 'object', properties: { x: { type: 'string' } }, required: ['x'] },
handler: (input) => ({ a: input.x }),
});
const toolB = tool('tool_b', {
description: 'Tool B',
input: { type: 'object', properties: { y: { type: 'number' } }, required: ['y'] },
handler: (input) => ({ b: input.y }),
});
serve({ tools: [toolA, toolB], port: 8080 });
API Endpoints
When tools are served, these endpoints are available:| Endpoint | Method | Description |
|---|---|---|
/health | GET | Health check |
/info | GET | Discovery (lists all tools with schemas) |
/tools/{name}/call | POST | Call a tool |
Call a Tool
Copy
curl -X POST http://localhost:8080/tools/get_weather/call \
-H "Content-Type: application/json" \
-d '{"input": {"location": "San Francisco", "units": "fahrenheit"}}'
Copy
{
"output": {
"location": "San Francisco",
"temperature": 65,
"units": "fahrenheit",
"condition": "foggy"
}
}
Discovery
Copy
curl http://localhost:8080/info
Copy
{
"tools": [
{
"name": "get_weather",
"type": "tool",
"description": "Get the current weather for a city",
"input": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "City name" },
"units": { "type": "string", "default": "celsius" }
},
"required": ["location"]
},
"output": {
"type": "object",
"properties": {
"temp": { "type": "number" },
"condition": { "type": "string" }
}
}
}
]
}
Complete Example
Copy
/**
* Runtime Tools Example
*
* Usage:
* npx tsx main.ts
*
* Test endpoints:
* curl http://localhost:8080/health
* curl http://localhost:8080/info
* curl -X POST http://localhost:8080/tools/get_weather/call \
* -H "Content-Type: application/json" \
* -d '{"input": {"location": "San Francisco"}}'
*/
import { tool, serve } from '@reminix/runtime';
const WEATHER_DATA: Record<string, { temp: number; condition: string }> = {
'san francisco': { temp: 65, condition: 'foggy' },
'new york': { temp: 45, condition: 'cloudy' },
'los angeles': { temp: 75, condition: 'sunny' },
};
const getWeather = tool('get_weather', {
description: 'Get the current weather for a city',
input: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' },
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
default: 'fahrenheit',
},
},
required: ['location'],
},
output: {
type: 'object',
properties: {
location: { type: 'string' },
temperature: { type: 'number' },
units: { type: 'string' },
condition: { type: 'string' },
},
},
handler: async (input) => {
const location = (input.location as string).toLowerCase();
const units = (input.units as string) || 'fahrenheit';
const weather = WEATHER_DATA[location];
if (!weather) {
return {
error: `Weather data not available for "${input.location}"`,
availableCities: Object.keys(WEATHER_DATA),
};
}
let temp = weather.temp;
if (units === 'celsius') {
temp = Math.round(((temp - 32) * 5) / 9);
}
return {
location: input.location as string,
temperature: temp,
units,
condition: weather.condition,
};
},
});
const calculate = tool('calculate', {
description: 'Perform basic math operations',
input: {
type: 'object',
properties: {
a: { type: 'number', description: 'First operand' },
b: { type: 'number', description: 'Second operand' },
operation: {
type: 'string',
enum: ['add', 'subtract', 'multiply', 'divide'],
description: 'Math operation',
},
},
required: ['a', 'b', 'operation'],
},
handler: (input) => {
const a = input.a as number;
const b = input.b as number;
const op = input.operation as string;
let result: number;
switch (op) {
case 'add':
result = a + b;
break;
case 'subtract':
result = a - b;
break;
case 'multiply':
result = a * b;
break;
case 'divide':
if (b === 0) return { error: 'Cannot divide by zero' };
result = a / b;
break;
default:
return { error: `Unknown operation: ${op}` };
}
return { a, b, operation: op, result };
},
});
serve({ tools: [getWeather, calculate], port: 8080 });