Optimizely.Opal.Tools 0.3.0

Prefix Reserved
dotnet add package Optimizely.Opal.Tools --version 0.3.0
                    
NuGet\Install-Package Optimizely.Opal.Tools -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Optimizely.Opal.Tools" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Optimizely.Opal.Tools" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Optimizely.Opal.Tools" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Optimizely.Opal.Tools --version 0.3.0
                    
#r "nuget: Optimizely.Opal.Tools, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Optimizely.Opal.Tools@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Optimizely.Opal.Tools&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Optimizely.Opal.Tools&version=0.3.0
                    
Install as a Cake Tool

Opal Tools SDK for C#

This SDK helps you create tool services for the Opal AI Assistant platform using C# and ASP.NET Core.

Installation

You can install the SDK using NuGet:

dotnet add package Optimizely.Opal.Tools

Quick Start

Here's a simple example of how to create a tools service with two tools:

using Microsoft.AspNetCore.Builder;
using Optimizely.Opal.Tools;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);

// Add the Opal Tools service
builder.Services.AddOpalToolService();

// Register individual tools
builder.Services.AddOpalTool<GreetingTool>();
builder.Services.AddOpalTool<TodaysDateTool>();

var app = builder.Build();

// Map the Opal Tools endpoints (creates /discovery and tool-specific endpoints)
app.MapOpalTools();

// Start the app
app.Run();

// Tool parameter models
public class GreetingParameters
{
    [Required]
    [Description("Name of the person to greet")]
    public string Name { get; set; } = string.Empty;
    
    [Description("Language for greeting (defaults to random)")]
    public string? Language { get; set; }
}

public class DateParameters
{
    [Description("Date format (defaults to ISO format)")]
    public string Format { get; set; } = "yyyy-MM-dd";
}

// Tool implementations
public class GreetingTool
{
    [OpalTool(Name = "greeting")]
    [Description("Greets a person in a random language (English, Spanish, or French)")]
    public object Greet(GreetingParameters parameters)
    {
        // Get parameters
        var name = parameters.Name;
        var language = parameters.Language;
        
        // If language not specified, choose randomly
        if (string.IsNullOrEmpty(language))
        {
            var random = new Random();
            var languages = new[] { "english", "spanish", "french" };
            language = languages[random.Next(languages.Length)];
        }
        
        // Generate greeting based on language
        string greeting;
        if (language.ToLower() == "spanish")
        {
            greeting = $"¡Hola, {name}! ¿Cómo estás?";
        }
        else if (language.ToLower() == "french")
        {
            greeting = $"Bonjour, {name}! Comment ça va?";
        }
        else // Default to English
        {
            greeting = $"Hello, {name}! How are you?";
        }
        
        return new
        {
            greeting,
            language
        };
    }
}

public class TodaysDateTool
{
    [OpalTool(Name = "todays-date")]
    [Description("Returns today's date in the specified format")]
    public object GetDate(DateParameters parameters)
    {
        // Get parameters
        var format = parameters.Format;
        
        // Get today's date
        var today = DateTime.Now;
        
        // Format the date
        var formattedDate = today.ToString(format);
        
        return new
        {
            date = formattedDate,
            format,
            timestamp = ((DateTimeOffset)today).ToUnixTimeSeconds()
        };
    }
}

Endpoints

When you call app.MapOpalTools(), the SDK automatically creates the following endpoints:

  • GET /discovery - Returns metadata about all registered tools in OpenAPI function format
  • POST /tools/{tool-name} - Executes the specified tool (one endpoint per registered tool)
  • POST /tools/callbacks/{ToolClassName}/{method-name} - Handles tool callbacks (not listed in discovery)

You can customize the endpoint prefix by passing it to MapOpalTools():

app.MapOpalTools("opal");
// Creates: GET /opal/tools/discovery, POST /opal/tools/{tool-name}, and POST /opal/tools/callbacks/{ToolClassName}/{method-name}

Authentication

To create a tool that requires authentication:

public class CalendarTool
{
    [OpalTool(Name = "get-calendar")]
    [Description("Gets user's calendar events")]
    [OpalAuthorization("google", "calendar")]
    public object GetCalendar(CalendarParameters parameters, OpalToolContext context)
    {
        // Use context.AuthorizationData.Provider and context.AuthorizationData.Credentials to authenticate
        var authData = context.AuthorizationData;
        if (authData != null)
        {
            // Access the authentication provider and credentials
            var provider = authData.Provider;
            var credentials = authData.Credentials;
            // ...
        }
        
        return new
        {
            events = new[] { "Event 1", "Event 2" }
        };
    }
}

Tool Request/Response Format

The tool request format follows the Opal Tools Management Service specification:

{
  "parameters": {
    "name": "John",
    "language": "spanish"
  },
  "auth": {
    "provider": "google",
    "credentials": {
      "token": "access_token_value"
    }
  },
  "environment": {
    "execution_mode": "headless"
  }
}

The response is the direct JSON serialization of the object returned by your tool method:

{
  "greeting": "¡Hola, John! ¿Cómo estás?",
  "language": "spanish"
}

API Reference

Models

  • OpalAuthorizationData: Authentication data from a request
  • OpalToolContext: Context object containing authentication data, environment information, and cancellation token for regular tools
  • OpalCallbackContext: Context object containing authentication data, environment information, and cancellation token for tool callbacks
  • ToolCallbackResponse: Structured response object for callbacks with message and optional link
  • IslandResponse: Response object for creating interactive UI components

Attributes

  • [OpalTool(name)]: Marks a method as an Opal tool
  • [OpalToolCallback]: Marks a method as an Opal tool callback (doesn't appear in discovery)
  • [OpalAuthorization(provider, scopeBundle, required)]: Specifies authentication requirements

Extension Methods

  • AddOpalToolService(): Extension method to add the tools service to IServiceCollection
  • AddOpalTool<T>(): Registers a specific tool class with the service collection
  • MapOpalTools(): Maps the discovery and tool endpoints to the application

Best Practices

  1. Use the [OpalTool] attribute on instance methods within tool classes
  2. Use the [Description] attribute on tool classes or methods to provide tool descriptions
  3. Define parameter models with clear property descriptions using [Description] attributes
  4. Return simple objects that can be serialized to JSON
  5. Use dependency injection in tool constructors for accessing services
  6. Handle errors gracefully within your tool methods
  7. Provide clear descriptions for tools and parameters
  8. Both synchronous and asynchronous methods are supported (use async Task<object> for async operations)

Advanced Usage

Headless Execution

Tools can be executed in different environments. The OpalToolContext provides an Environment property that gives information about the execution environment. One common scenario is to check if a tool is being run in a "headless" mode, where no user interaction is possible.

You can check the ExecutionMode property of the Environment object to determine if you should return a direct response or an interactive one (like an Island).

public class MyTool
{
    [OpalTool(Name = "my-interactive-tool")]
    [Description("A tool that can be interactive or headless")]
    public object MyInteractiveTool(MyParameters parameters, OpalToolContext context)
    {
        if (context.Environment?.ExecutionMode == "headless")
        {
            // Headless mode, return a direct response
            return new { message = "This is a headless response." };
        }

        // Interactive mode, return an island
        return new IslandResponse
        {
            // ... island configuration ...
        };
    }
}

Tool Naming

Tool names are automatically derived from method names, with underscores converted to hyphens. You can override this with the Name parameter:

public class MyTool
{
    [OpalTool] // Creates tool named "MyMethod"
    public object MyMethod(Parameters parameters) { ... }
    
    [OpalTool(Name = "custom-name")] // Creates tool named "custom-name"
    public object AnyMethodName(Parameters parameters) { ... }
}

Asynchronous Tools

The SDK supports both synchronous and asynchronous tool methods:

public class AsyncTool
{
    [OpalTool(Name = "async-example")]
    [Description("An example of an asynchronous tool")]
    public async Task<object> AsyncMethod(Parameters parameters)
    {
        await SomeAsyncOperation();
        return new { result = "success" };
    }
}

Custom Parameter Validation

You can use standard .NET validation attributes:

public class GreetingParameters
{
    [Required]
    [StringLength(100)]
    public string Name { get; set; } = string.Empty;
    
    [RegularExpression("english|spanish|french", ErrorMessage = "Language must be english, spanish, or french")]
    public string? Language { get; set; }
}

Dependency Injection

Tools can use dependency injection by declaring dependencies in their constructors:

public class WeatherTool
{
    private readonly IWeatherService _weatherService;
    private readonly ILogger<WeatherTool> _logger;
    
    public WeatherTool(IWeatherService weatherService, ILogger<WeatherTool> logger)
    {
        _weatherService = weatherService;
        _logger = logger;
    }
    
    [OpalTool(Name = "get-weather")]
    [Description("Gets the weather for a location")]
    public async Task<object> GetWeather(WeatherParameters parameters)
    {
        _logger.LogInformation("Getting weather for {Location}", parameters.Location);
        var forecast = await _weatherService.GetForecastAsync(parameters.Location);
        
        return new
        {
            location = parameters.Location,
            forecast
        };
    }
}

Tool Callbacks

Tool callbacks are special methods that handle responses from interaction islands but don't appear in the discovery endpoint. They use a different request format and can return structured responses:

public class WeatherTool
{
    [OpalTool(Name = "weather-tool")]
    [Description("Get current weather with interactive form")]
    public object GetWeather(WeatherParameters parameters)
    {
        return new IslandResponse
        {
            Configuration =
            {
                Fields =
                [
                    new() { Name = "location", Label = "Location", Type = "string", Value = parameters.Location },
                ],
                Actions =
                [
                    new() { Name = "get-weather", Label = "Get Weather", Operation = "create", Endpoint = nameof(ProcessWeatherCallback) }
                ]
            }
        };
    }
    
    [OpalToolCallback]
    public ToolCallbackResponse ProcessWeatherCallback(WeatherCallbackData data, OpalCallbackContext context)
    {
        // Process the callback with data from the interaction island
        return new ToolCallbackResponse
        {
            Message = $"The weather in {data.Location} is 22°C",
            Link = $"https://weather.example.com/location/{Uri.EscapeDataString(data.Location)}"
        };
    }
}

public class WeatherCallbackData
{
    public string Location { get; set; } = string.Empty;
}

Callback Request Format: Callbacks receive requests in a different format than regular tools:

{
  "data": {
    "location": "New York"
  },
  "auth": {
    "provider": "google",
    "credentials": {
      "token": "access_token_value"
    }
  },
  "environment": {
    "execution_mode": "headless"
  }
}

Callback Response Options: Callbacks can return either:

  1. A simple string message
  2. A ToolCallbackResponse object with structured data:
{
  "message": "The weather in New York is 22°C",
  "link": "https://weather.example.com/location/New York"
}

Key characteristics of callbacks:

  • Use the [OpalToolCallback] attribute instead of [OpalTool]
  • Don't appear in the discovery endpoint
  • Are accessible at /tools/callbacks/{ToolClassName}/{MethodName}
  • Receive data in { "data": {...}, "auth": {...}, "environment": {...} } format
  • Can return ToolCallbackResponse for structured feedback

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 183 7/4/2025
0.2.0 102 7/4/2025