Optimizely Connect Platform (OCP) SDK for OPAL
A TypeScript SDK for building Opal tools in Optimizely Connect Platform. This SDK provides decorators, abstractions, and utilities to simplify the development.
- 🎯 Decorator-based Tool Registration - Use
@tool
and@interaction
decorators to easily register functions - 🔧 Type-safe Development - Full TypeScript support with comprehensive type definitions
- 🏗️ Abstract Base Classes - Extend
ToolFunction
for standardized request processing - 🔐 Authentication Support - OptiID authentication
- �️ Authorization Support - Bearer token tool authorization
- 📝 Parameter Validation - Define and validate tool parameters with types
- 🧪 Comprehensive Testing - Fully tested with Jest
npm install @optimizely-opal/opal-ocp-sdk
or
yarn add @optimizely-opal/opal-ocp-sdk
import { ToolFunction, tool, interaction, ParameterType, InteractionResult, OptiIdAuthData } from '@optimizely-opal/opal-ocp-sdk';
export class MyToolFunction extends ToolFunction {
// Register a simple tool without authentication
@tool({
name: 'create_task',
description: 'Creates a new task in the system',
endpoint: '/create-task',
parameters: [
{
name: 'title',
type: ParameterType.String,
description: 'The task title',
required: true
},
{
name: 'priority',
type: ParameterType.String,
description: 'Task priority level',
required: false
}
]
})
async createTask(params: { title: string; priority?: string }, authData?: OptiIdAuthData, bearerToken?: string) {
return {
id: '123',
title: params.title,
priority: params.priority || 'medium'
};
}
// Register a tool with OptiID authentication
@tool({
name: 'secure_task',
description: 'Creates a secure task with OptiID authentication',
endpoint: '/secure-task',
parameters: [
{
name: 'title',
type: ParameterType.String,
description: 'The task title',
required: true
}
],
authRequirements: [
{
provider: 'OptiID',
scopeBundle: 'tasks',
required: true
}
]
})
async createSecureTask(params: { title: string }, authData?: OptiIdAuthData, bearerToken?: string) {
if (!authData) {
throw new Error('OptiID authentication required');
}
const { customerId, instanceId, accessToken } = authData.credentials;
return {
id: '456',
title: params.title,
customerId,
instanceId
};
}
// Register an interaction
@interaction({
name: 'task_webhook',
endpoint: '/webhook/task'
})
async handleTaskWebhook(data: any): Promise<InteractionResult> {
return new InteractionResult(
`Task ${data.taskId} was updated`,
`https://app.example.com/tasks/${data.taskId}`
);
}
}
The perform
method handles all requests automatically and routes them to your registered tools and interactions.
Tools are functions that can be discovered and executed through the OCP platform. They:
- Have a name, description, and endpoint
- Define parameters with types and validation
- Can require authentication
- Return structured responses
Interactions are event handlers that process incoming data (like webhooks):
- Have a name and endpoint
- Process unstructured data
- Return interaction results with messages and optional links
Supported parameter types:
enum ParameterType {
String = 'string',
Integer = 'integer',
Number = 'number',
Boolean = 'boolean',
List = 'list',
Dictionary = 'object'
}
The SDK supports authentication and authorization mechanisms:
OptiID provides user authentication with type safety. This is the only user authentication provider currently supported in Opal:
interface AuthRequirementConfig {
provider: string; // 'OptiID'
scopeBundle: string; // e.g., 'calendar', 'tasks'
required?: boolean; // default: true
}
The SDK automatically extracts Bearer tokens from the Authorization
header for tool authorization. When users register tools in Opal, they can specify a Bearer token that Opal will include in requests to validate the tool's identity:
@tool({
name: 'secure_tool',
description: 'Tool that validates requests from Opal',
endpoint: '/secure-endpoint',
parameters: [
{ name: 'data', type: ParameterType.String, description: 'Data to process', required: true }
]
})
async secureToolHandler(
params: { data: string },
authData?: OptiIdAuthData,
bearerToken?: string
) {
// Validate that the request is from an authorized Opal instance
const expectedToken = process.env.OPAL_TOOL_TOKEN;
if (!bearerToken || bearerToken !== expectedToken) {
throw new Error('Unauthorized: Invalid or missing Bearer token');
}
// Process the request knowing it's from a trusted Opal instance
return {
status: 'success',
data: `Processed: ${params.data}`,
authorizedBy: 'Opal'
};
}
You can use both OptiID authentication (for user context) and Bearer tokens (for tool authorization) together:
@tool({
name: 'dual_auth_tool',
description: 'Tool with both user authentication and tool authorization',
endpoint: '/dual-auth',
parameters: [],
authRequirements: [
{ provider: 'OptiID', scopeBundle: 'calendar', required: true }
]
})
async dualAuthOperation(
params: unknown,
authData?: OptiIdAuthData,
bearerToken?: string
) {
// First, validate the tool authorization (request from Opal)
const expectedToken = process.env.OPAL_TOOL_TOKEN;
if (!bearerToken || bearerToken !== expectedToken) {
throw new Error('Unauthorized: Invalid tool authorization');
}
// Then, validate user authentication
if (!authData) {
throw new Error('OptiID authentication required');
}
const { customerId, accessToken } = authData.credentials;
// Process with both tool and user authorization
return {
message: 'Authorized request processed',
customerId,
toolAuthorized: true
};
}
Bearer Token Usage:
- Set by users when registering tools in Opal
- Used to authorize tool requests from Opal instances
- Validates that requests are coming from trusted Opal sources
All tool and interaction handler methods follow this signature pattern:
async handlerMethod(
params: TParams, // Tool parameters or interaction data
authData?: OptiIdAuthData, // OptiID user authentication data (if authenticated)
bearerToken?: string // Bearer token for tool authorization (if provided)
): Promise<TResult>
- params: The input parameters for tools, or interaction data for webhooks
- authData: Available when OptiID user authentication is configured and successful
- bearerToken: Used for tool authorization - validates that requests are from authorized Opal instances
Registers a method as a discoverable tool.
interface ToolConfig {
name: string;
description: string;
parameters: ParameterConfig[];
authRequirements?: AuthRequirementConfig[];
endpoint: string;
}
Registers a method as an interaction handler.
interface InteractionConfig {
name: string;
endpoint: string;
}
Abstract base class for OCP functions:
export abstract class ToolFunction extends Function {
public async perform(): Promise<Response>;
}
Extend this class and implement your OCP function. The perform
method automatically routes requests to registered tools.
Key model classes with generic type support:
-
Tool<TAuthData>
- Represents a registered tool with typed auth data -
Interaction<TAuthData>
- Represents an interaction handler with typed auth data -
Parameter
- Defines tool parameters -
AuthRequirement
- Defines authentication needs -
InteractionResult
- Response from interactions -
OptiIdAuthData
- OptiID specific authentication data
The SDK automatically provides a discovery endpoint at /discovery
that returns all registered tools in the proper OCP format:
{
"functions": [
{
"name": "create_task",
"description": "Creates a new task in the system",
"parameters": [
{
"name": "title",
"type": "string",
"description": "The task title",
"required": true
},
{
"name": "priority",
"type": "string",
"description": "Task priority level",
"required": false
}
],
"endpoint": "/create-task",
"http_method": "POST"
},
{
"name": "secure_task",
"description": "Creates a secure task with OptiID authentication",
"parameters": [
{
"name": "title",
"type": "string",
"description": "The task title",
"required": true
}
],
"endpoint": "/secure-task",
"http_method": "POST",
"auth_requirements": [
{
"provider": "OptiID",
"scope_bundle": "tasks",
"required": true
}
]
}
]
}
- Node.js >= 22.0.0
- TypeScript 5.x
yarn build
# Run tests
yarn test
# Run tests in watch mode
yarn test:watch
# Run tests with coverage
yarn test:coverage
yarn lint
import { ToolFunction, tool, interaction, ParameterType, OptiIdAuthData, InteractionResult } from '@optimizely-opal/opal-ocp-sdk';
export class AuthenticatedFunction extends ToolFunction {
// OptiID authentication example
@tool({
name: 'secure_operation',
description: 'Performs a secure operation with OptiID',
endpoint: '/secure',
parameters: [],
authRequirements: [{ provider: 'OptiID', scopeBundle: 'tasks', required: true }]
})
async secureOperation(params: unknown, authData?: OptiIdAuthData, bearerToken?: string) {
if (!authData) throw new Error('OptiID authentication required');
const { customerId, accessToken } = authData.credentials;
// Use OptiID credentials for API calls
return { success: true, customerId };
}
// Interaction with authentication example
@interaction({
name: 'authenticated_webhook',
endpoint: '/secure-webhook'
})
async handleSecureWebhook(data: any, authData?: OptiIdAuthData, bearerToken?: string): Promise<InteractionResult> {
if (!authData) {
return new InteractionResult('Authentication required for webhook processing');
}
const { customerId } = authData.credentials;
// Process webhook data with authentication context
return new InteractionResult(
`Webhook processed for customer ${customerId}: ${data.eventType}`,
`https://app.example.com/events/${data.eventId}`
);
}
}