SDK for creating Opal-compatible tools services
npm install @optimizely-opal/opal-tools-sdkThis SDK simplifies the creation of tools services compatible with the Opal Tools Management Service.
- Modern registerTool API with Zod schemas for type-safe tool definitions
- Legacy @tool decorator API for backwards compatibility
- Automatic type inference with Zod (registerTool only)
- Adaptive Block Document support for rich interactive UIs
- Runtime validation with Zod (registerTool only)
- Automatic discovery endpoint generation
- Authentication helpers
- Express integration
``bash`
npm install @optimizely-opal/opal-tools-sdk express zod
`typescript
import express from "express";
import { z } from "zod";
import { ToolsService, registerTool } from "@optimizely-opal/opal-tools-sdk";
const app = express();
const toolsService = new ToolsService(app);
// ✨ Types are automatically inferred from the inputSchema!
const getWeather = registerTool(
"get_weather",
{
description: "Gets current weather for a location",
inputSchema: {
location: z.string().describe("City name or location"),
units: z
.enum(["metric", "imperial"])
.optional()
.describe("Temperature units"),
},
},
async (params) => {
// params.location is typed as string
// params.units is typed as 'metric' | 'imperial' | undefined
return { temperature: 22, condition: "sunny" };
},
);
app.listen(3000);
`
`typescript
import { ToolsService, tool, ParameterType } from '@optimizely-opal/opal-tools-sdk';
interface WeatherParameters {
location: string;
units?: string;
}
@tool({
name: 'get_weather',
description: 'Gets current weather for a location',
parameters: [
{
name: 'location',
type: ParameterType.String,
description: 'City name or location',
required: true,
},
{
name: 'units',
type: ParameterType.String,
description: 'Temperature units',
required: false,
},
],
})
async function getWeather(parameters: WeatherParameters) {
// Implementation...
return { temperature: 22, condition: 'sunny' };
}
// Discovery endpoint is automatically created at /discovery
`
Both APIs support authentication. The second parameter of your handler receives the extra context object containing auth data.
`typescript
import { z } from "zod";
const getCalendarEvents = registerTool(
"get_calendar_events",
{
description: "Gets calendar events for a date",
inputSchema: {
date: z.string().describe("Date in YYYY-MM-DD format"),
},
authRequirements: {
provider: "google",
scopeBundle: "calendar",
required: true,
},
},
async (params, extra) => {
// Access auth data from extra context
const token = extra?.auth?.credentials?.access_token;
// Check execution mode if needed
const mode = extra?.mode; // 'headless' | 'interactive'
// Use token to make authenticated requests
return { events: ["Meeting at 10:00", "Lunch at 12:00"] };
},
);
`
`typescript`
@tool({
name: 'get_calendar_events',
description: 'Gets calendar events',
parameters: [
{
name: 'date',
type: ParameterType.String,
description: 'Date in YYYY-MM-DD format',
required: true,
},
],
authRequirements: {
provider: 'google',
scopeBundle: 'calendar',
required: true,
},
})
async function getCalendarEvents(params: any, environment?: any) {
const token = environment?.auth?.credentials?.access_token;
return { events: [] };
}
Adaptive Block Documents enable rich, interactive UI responses with forms, buttons, and dynamic content.
`typescript
import { z } from "zod";
import { registerTool, Block } from "@optimizely-opal/opal-tools-sdk";
const createTask = registerTool(
"create_task",
{
description: "Create a new task",
type: "block", // Specify this is an Adaptive Block tool
inputSchema: {
title: z.string().describe("Task title"),
description: z.string().optional().describe("Task description"),
},
},
async (params) => {
// Return a BlockResponse (plain object with content/data/artifact/etc)
return {
content: Block.Document({
children: [
Block.Heading({ children: "Task Created!", level: "1" }),
Block.Text({ children: Created: ${params.title} }),`
Block.Input({
name: "notes",
placeholder: "Add notes...",
value: params.description || "",
}),
],
actions: [
Block.Action({ name: "save", children: "Save Changes" }),
Block.Action({
name: "delete",
children: "Delete",
variant: "danger",
}),
],
}),
data: { task_id: "123", created_at: new Date().toISOString() },
artifact: {
type: "task",
id: "task-123",
data: { title: params.title },
},
};
},
);
The SDK provides a type-safe Block namespace with factory functions for all components:
- Block.Document() - Root container with children and actionsBlock.Heading()
- - Headings with levels 1-6Block.Text()
- - Text contentBlock.Input()
- - Text input fieldsBlock.Textarea()
- - Multi-line text areasBlock.Checkbox()
- - Checkbox inputsBlock.Select()
- - Dropdown selectsBlock.Button()
- - Action buttonsBlock.Action()
- - Document-level actionsBlock.List()
- - Ordered/unordered listsBlock.Table()
- - Data tables
- And more...
See the generated src/block.ts for the complete list and TypeScript types.
The Adaptive Block types are auto-generated from the JSON schema. To regenerate:
`bash`
npm run generate:block
This reads block-document-spec.json and generates TypeScript interfaces and factory functions in src/block.ts.
Note: Don't edit src/block.ts manually - it will be overwritten on regeneration.
The SDK provides comprehensive TypeScript type definitions:
- AuthData - Provider and credentials informationCredentials
- - Access tokens and org detailsEnvironment
- - Execution context with auth data
- ParameterType - Enum for parameter types (String, Number, Boolean, List, Dictionary)Parameter
- - Tool parameter definitionsFunction
- - Complete tool function definitions
All Block Document components have full TypeScript interfaces with type checking and IDE autocomplete.
Modern API for defining tools with Zod schemas.
Parameters:
- name: string - Tool name (required)options: ToolOptions
- - Configuration objectdescription: string
- - Tool description (required)inputSchema: Record
- - Zod schema for parameters (required)type?: 'json' | 'block'
- - Response type (default: 'json')authRequirements?
- - Authentication requirementshandler: (params, extra?) => Result
- - Tool implementationparams
- - Validated input parameters (typed from schema)extra?
- - Optional extra contextmode: 'headless' | 'interactive'
- - Execution modeauth?: { provider, credentials }
- - Auth data if provided
Returns: The handler function with full type inference
Legacy decorator for defining tools.
Options:
- name: string - Tool namedescription: string
- - Tool descriptionparameters: ParameterDefinition[]
- - Parameter definitionsauthRequirements?
- - Authentication requirements
Express service that manages tool registration and creates discovery endpoints.
`typescript``
const toolsService = new ToolsService(app);
// Automatically creates /discovery endpoint
MIT