npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dainprotocol/service-sdk

v2.0.89

Published

DAIN Service SDK

Readme

DAIN Service SDK

npm version License: ISC

The official TypeScript SDK for building AI-powered services on the DAIN Protocol. Create intelligent services that can be discovered and used by AI agents across multiple platforms.

📋 Table of Contents

✨ Features

  • 🚀 Multi-Runtime Support - Works seamlessly on Node.js, Deno, Cloudflare Workers, and Next.js
  • 🔧 Type-Safe Tools - Define tools with Zod schemas for automatic validation and type inference
  • 🔐 OAuth2 Integration - Built-in OAuth2 support with flexible storage adapters
  • 🔌 Plugin System - Extend functionality with crypto, citations, time, and custom plugins
  • 📡 Real-Time Streaming - Server-Sent Events (SSE) for streaming responses
  • 🔄 Long-Running Processes - State machine-based process management
  • 💳 Payment Integration - Built-in support for Stripe payments
  • 🎨 UI Components - Return rich UI responses alongside data
  • 📊 Contexts & Datasources - Provide dynamic context and data to AI agents
  • 🛡️ Signature-Based Auth - Secure authentication using Ed25519 signatures

📦 Installation

npm install @dainprotocol/service-sdk zod

Optional Dependencies

For OAuth2 with persistent storage:

npm install @dainprotocol/oauth2-token-manager @dainprotocol/oauth2-storage-drizzle

For process management with Redis:

npm install ioredis

🚀 Quick Start

Creating a Simple Weather Service (Node.js)

import { defineDAINService } from '@dainprotocol/service-sdk';
import { z } from 'zod';

// Define a tool
const getWeatherTool = {
  id: 'get-weather',
  name: 'Get Weather',
  description: 'Fetches current weather for a city',
  input: z.object({
    city: z.string().describe('The name of the city'),
  }),
  output: z.object({
    temperature: z.number().describe('Temperature in Celsius'),
    condition: z.string().describe('Weather condition'),
  }),
  pricing: { pricePerUse: 0.01, currency: 'USD' },
  handler: async ({ city }, agentInfo) => {
    // Your logic here
    return {
      text: `The weather in ${city} is 22°C and Sunny`,
      data: { temperature: 22, condition: 'Sunny' },
      ui: undefined,
    };
  },
};

// Create and start the service
const service = defineDAINService({
  metadata: {
    title: 'Weather Service',
    description: 'A service for weather information',
    version: '1.0.0',
    author: 'Your Name',
    tags: ['weather'],
  },
  identity: {
    apiKey: process.env.DAIN_API_KEY,
  },
  tools: [getWeatherTool],
});

// Start the service
service.startNode({ port: 3000 }).then(() => {
  console.log('Weather service running on port 3000');
});

Using the Client SDK

import { DainServiceConnection, DainClientAuth } from '@dainprotocol/service-sdk/client';

// Initialize authentication
const auth = new DainClientAuth({
  apiKey: process.env.DAIN_API_KEY,
});

// Connect to a service
const service = new DainServiceConnection('http://localhost:3000', auth);

// Call a tool
const result = await service.callTool('get-weather', {
  city: 'San Francisco',
});

console.log(result.text); // "The weather in San Francisco is 22°C and Sunny"
console.log(result.data); // { temperature: 22, condition: 'Sunny' }

🧩 Core Concepts

Services

A service is a collection of tools, contexts, datasources, and widgets that provide specific functionality to AI agents.

const service = defineDAINService({
  metadata: { /* ... */ },
  identity: { /* ... */ },
  tools: [ /* ... */ ],
  contexts: [ /* ... */ ],
  datasources: [ /* ... */ ],
  widgets: [ /* ... */ ],
});

Tools

Tools are functions that AI agents can call. Each tool has:

  • A unique ID
  • Input/output schemas (using Zod)
  • A handler function
  • Optional pricing information
const myTool = {
  id: 'my-tool',
  name: 'My Tool',
  description: 'Does something useful',
  input: z.object({ param: z.string() }),
  output: z.object({ result: z.string() }),
  handler: async (input, agentInfo, context) => {
    return {
      text: 'Human-readable response',
      data: { result: 'structured data' },
      ui: { /* optional UI component */ },
    };
  },
};

Agent Info

Every tool handler receives agentInfo with details about the calling agent:

interface AgentInfo {
  agentId: string;       // Unique agent identifier
  address: string;       // Agent's blockchain address
  smartAccountPDA?: string;
  id: string;
  webhookUrl?: string;   // For sending async updates
}

Tool Context

The context parameter provides access to:

  • The Hono app instance
  • OAuth2 client for authenticated API calls
  • Custom extra data
  • UI update functions
  • Process management
interface ToolContext {
  app: Hono;
  oauth2Client?: OAuth2Client;
  extraData?: any;
  updateUI?: (update: { ui: any }) => Promise<void>;
  addProcess?: (processId: string) => Promise<void>;
}

🌐 Multi-Runtime Support

The SDK automatically adapts to your runtime environment through conditional exports.

Node.js

import { defineDAINService } from '@dainprotocol/service-sdk';

const service = defineDAINService({ /* config */ });
await service.startNode({ port: 3000 });

Deno

import { defineDAINService } from '@dainprotocol/service-sdk';

const service = defineDAINService({ /* config */ });
await service.startDeno({ port: 3000 });

Cloudflare Workers

import { defineDAINService } from '@dainprotocol/service-sdk';

const service = defineDAINService({ /* config */ });

export default {
  fetch: service.startWorkers(),
};

Next.js (App Router)

// app/api/dain/[...dain]/route.ts
import { createNextDainService } from '@dainprotocol/service-sdk/next';

const { GET, POST } = createNextDainService({
  metadata: { /* ... */ },
  identity: { /* ... */ },
  tools: [ /* ... */ ],
});

export { GET, POST };

🔧 Tools

Creating Tools

Use the createTool helper for better type inference:

import { createTool } from '@dainprotocol/service-sdk';

const weatherTool = createTool({
  id: 'get-weather',
  name: 'Get Weather',
  description: 'Get current weather',
  input: z.object({
    lat: z.number(),
    lon: z.number(),
  }),
  output: z.object({
    temp: z.number(),
    description: z.string(),
  }),
  handler: async ({ lat, lon }) => {
    const response = await fetch(
      `https://api.openweather.org/data/2.5/weather?lat=${lat}&lon=${lon}`
    );
    const data = await response.json();

    return {
      text: `Temperature: ${data.main.temp}°C`,
      data: {
        temp: data.main.temp,
        description: data.weather[0].description,
      },
      ui: undefined,
    };
  },
});

Tool Interfaces

Tools can implement standardized interfaces for better interoperability:

import { createToolWithInterface, ToolInterfaceType } from '@dainprotocol/service-sdk';

const searchTool = createToolWithInterface({
  id: 'search-docs',
  name: 'Search Documentation',
  description: 'Search through documentation',
  interface: ToolInterfaceType.KNOWLEDGE_SEARCH,
  input: z.object({
    query: z.string(),
  }),
  output: z.object({
    results: z.array(z.object({
      title: z.string(),
      content: z.string(),
      citations: z.array(CitationSchema),
    })),
  }),
  handler: async ({ query }) => {
    // Implementation
  },
});

Actionable Tools

Tools that send messages where users can trigger system actions (buttons, callbacks, links). These tools automatically enforce a standard schema for interoperability with automation systems.

Use Cases:

  • Telegram bot with inline buttons
  • Slack interactive messages
  • Discord message components
  • Email with action links

Quick Start with buildActionableTool:

import { buildActionableTool, z } from '@dainprotocol/service-sdk';

const sendTelegramMessage = buildActionableTool({
  id: 'send-telegram-message',
  name: 'Send Telegram Message',
  description: 'Send a message with interactive buttons',

  // Add platform-specific fields
  additionalFields: {
    parse_mode: z.enum(['HTML', 'Markdown']).optional(),
  },

  output: z.object({
    messageId: z.number(),
  }),

  handler: async (input, agentInfo, context) => {
    // input includes: recipient, message, actions, metadata, parse_mode
    const { recipient, message, actions } = input;

    // Build inline keyboard from actions
    const keyboard = actions?.map(action => ({
      text: action.label,
      callback_data: action.value,
    }));

    // Send via your messaging API
    const result = await sendToTelegram(recipient, message, keyboard);

    return {
      text: `Message sent to ${recipient}`,
      data: { messageId: result.message_id },
    };
  },
});

Standard Fields (automatically included):

  • recipient (required): Who receives the message (chatId, channel, email)
  • message (required): The message text
  • actions (optional): Interactive buttons/links array
  • metadata (optional): Automation context (auto-injected)

Action Types:

  • approve - Approval button
  • reject - Rejection button
  • callback - Custom callback
  • url - External link
  • custom - Custom action

Usage in Automations:

<action
  tool="send-telegram-message"
  recipient="{{config.chatId}}"
  message="Bitcoin is ${{prev.price}}. Buy?"
  actions='[
    {"label": "✅ Buy", "type": "approve", "value": "buy"},
    {"label": "❌ Skip", "type": "reject", "value": "skip"}
  ]'/>

See example/actionable-tool-example.ts for complete examples (Telegram, Slack, Email).

Tool Pricing

Add pricing to monetize your tools:

const paidTool = {
  id: 'premium-analysis',
  name: 'Premium Analysis',
  description: 'Advanced data analysis',
  pricing: {
    pricePerUse: 0.50,  // $0.50 per use
    currency: 'USD',
  },
  // ... rest of tool config
};

Toolboxes

Group related tools into toolboxes:

import { createToolbox } from '@dainprotocol/service-sdk';

const weatherToolbox = createToolbox({
  id: 'weather-toolbox',
  name: 'Weather Tools',
  description: 'Complete weather information toolkit',
  tools: ['get-weather', 'get-forecast', 'get-alerts'],
  metadata: {
    complexity: 'simple',
    applicableFields: ['weather', 'climate'],
  },
  recommendedPrompt: 'Use these tools to get comprehensive weather information',
});

🔐 OAuth2 Integration

The SDK includes comprehensive OAuth2 support with automatic token management and refresh.

Basic OAuth2 Setup

import { defineDAINService } from '@dainprotocol/service-sdk';
import { InMemoryStorageAdapter } from '@dainprotocol/oauth2-token-manager';

const service = defineDAINService({
  metadata: { /* ... */ },
  identity: { /* ... */ },
  tools: [ /* ... */ ],
  oauth2: {
    baseUrl: 'https://your-service.com',
    tokenStore: new InMemoryStorageAdapter(),
    providers: {
      github: {
        clientId: process.env.GITHUB_CLIENT_ID!,
        clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        authorizationUrl: 'https://github.com/login/oauth/authorize',
        tokenUrl: 'https://github.com/login/oauth/access_token',
        scopes: ['user', 'repo'],
        reason: 'Access your GitHub repositories',
        requiredTools: ['get-repos', 'create-gist'],
      },
    },
  },
});

Persistent Storage (PostgreSQL)

import { DrizzleStorageAdapter } from '@dainprotocol/oauth2-storage-drizzle';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';

const sql = postgres(process.env.DATABASE_URL!);
const db = drizzle(sql);

const service = defineDAINService({
  oauth2: {
    baseUrl: 'https://your-service.com',
    tokenStore: new DrizzleStorageAdapter(db, { dialect: 'postgres' }),
    providers: { /* ... */ },
  },
  // ... rest of config
});

Custom Token Paths

For OAuth2 providers with non-standard response formats (like Slack):

const service = defineDAINService({
  oauth2: {
    baseUrl: 'https://your-service.com',
    providers: {
      slack: {
        clientId: process.env.SLACK_CLIENT_ID!,
        clientSecret: process.env.SLACK_CLIENT_SECRET!,
        authorizationUrl: 'https://slack.com/oauth/v2/authorize',
        tokenUrl: 'https://slack.com/api/oauth.v2.access',
        scopes: ['chat:write', 'channels:read'],
        // Custom paths for extracting tokens from response
        tokenPaths: {
          accessToken: 'authed_user.access_token',
          tokenType: 'token_type',
          scope: 'scope',
        },
      },
    },
  },
  // ... rest of config
});

Using OAuth2 in Tools

import { createOAuth2Tool } from '@dainprotocol/service-sdk';

const getGitHubProfileTool = createOAuth2Tool({
  id: 'get-github-profile',
  name: 'Get GitHub Profile',
  description: 'Get the authenticated user GitHub profile',
  provider: 'github',
  input: z.object({}),
  output: z.object({
    login: z.string(),
    name: z.string(),
    email: z.string().nullable(),
    bio: z.string().nullable(),
  }),
  handler: async (input, agentInfo, context) => {
    const accessToken = await context.oauth2Client!.getAccessToken(
      'github',
      `${agentInfo.agentId}@dain.local`
    );

    const response = await fetch('https://api.github.com/user', {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        Accept: 'application/vnd.github.v3+json',
      },
    });

    const data = await response.json();

    return {
      text: `GitHub Profile: ${data.login}`,
      data: {
        login: data.login,
        name: data.name,
        email: data.email,
        bio: data.bio,
      },
      ui: undefined,
    };
  },
});

OAuth2 Provider Options

interface OAuth2ProviderConfig {
  clientId: string;
  clientSecret: string;
  authorizationUrl: string;
  tokenUrl: string;
  scopes: string[];
  usePKCE?: boolean;              // Use PKCE flow (recommended for public clients)
  useBasicAuth?: boolean;         // Use Basic Auth for token exchange
  reason?: string;                // Explain why OAuth is needed (shown to users)
  requiredTools?: string[];       // Tools that require this OAuth connection
  extraAuthParams?: Record<string, string>;  // Additional auth parameters
  responseRootKey?: string;       // Root key for token response
  tokenPaths?: {                  // Custom token extraction paths
    accessToken?: string | string[];
    refreshToken?: string | string[];
    expiresIn?: string | string[];
    tokenType?: string | string[];
    scope?: string | string[];
  };
  onSuccess?: (agentId: string, tokens: OAuth2Tokens) => Promise<void>;
}

🔌 Plugins

Plugins extend functionality on both the service and client side.

Built-in Plugins

Crypto Plugin

import { CryptoPlugin } from '@dainprotocol/service-sdk/plugins';

const cryptoPlugin = new CryptoPlugin();

const service = defineDAINService({
  plugins: [cryptoPlugin],
  // ... rest of config
});

Provides:

  • Message signing and verification
  • Encryption/decryption
  • Hash generation
  • Key management

Citations Plugin

import { CitationsPlugin } from '@dainprotocol/service-sdk/plugins';

const citationsPlugin = new CitationsPlugin({
  namespace: 'my-service',
});

const service = defineDAINService({
  plugins: [citationsPlugin],
  // ... rest of config
});

Provides:

  • Citation tracking
  • Source verification
  • Citation formatting

Time Plugin

import { TimePlugin } from '@dainprotocol/service-sdk/plugins';

const timePlugin = new TimePlugin();

const service = defineDAINService({
  plugins: [timePlugin],
  // ... rest of config
});

Provides:

  • Timezone conversion
  • Timestamp validation
  • Time-based operations

Creating Custom Plugins

import { DainPlugin } from '@dainprotocol/service-sdk';

class MyCustomPlugin implements DainPlugin {
  id = 'my-custom-plugin';

  async initialize(service: DAINService): Promise<void> {
    console.log('Plugin initialized');
  }

  async processInputClient?(input: any): Promise<any> {
    // Process input on the client side
    return { myData: 'client-processed' };
  }

  async processInputService?(input: any, agentInfo: AgentInfo): Promise<any> {
    // Process input on the service side
    return { myData: 'service-processed' };
  }

  async processOutputService?(output: any, agentInfo: AgentInfo): Promise<any> {
    // Process output before sending to client
    return output;
  }
}

// Use it
const service = defineDAINService({
  plugins: [new MyCustomPlugin()],
  // ... rest of config
});

📡 Client SDK

Connecting to a Service

import { DainServiceConnection, DainClientAuth } from '@dainprotocol/service-sdk/client';

const auth = new DainClientAuth({
  apiKey: process.env.DAIN_API_KEY,
});

const service = new DainServiceConnection('http://localhost:3000', auth, {
  plugins: [/* optional plugins */],
});

Getting Service Metadata

const metadata = await service.getMetadata();
console.log(metadata.title);
console.log(metadata.description);
console.log(metadata.version);

Listing Available Tools

const tools = await service.getTools();
tools.forEach(tool => {
  console.log(`${tool.name}: ${tool.description}`);
});

Calling Tools

const result = await service.callTool('tool-id', {
  param1: 'value1',
  param2: 'value2',
});

console.log(result.text);  // Human-readable response
console.log(result.data);  // Structured data
console.log(result.ui);    // Optional UI component

Streaming Responses

const stream = await service.callToolStream('tool-id', {
  query: 'What is the weather?',
});

for await (const chunk of stream) {
  if (chunk.type === 'text-delta') {
    process.stdout.write(chunk.textDelta);
  } else if (chunk.type === 'tool-result') {
    console.log('\nFinal result:', chunk.result);
  }
}

Working with Contexts

const contexts = await service.getContexts();
const userContext = await service.getContext('user-context', {
  param: 'value',
});

console.log(userContext.data);

Working with Datasources

const datasources = await service.getDatasources();
const data = await service.getDatasource('my-datasource', {
  filter: 'active',
});

console.log(data.data);

OAuth2 from Client

// Check available OAuth2 providers
const providers = await service.getOAuth2Providers();

// Initiate OAuth flow
const authResult = await service.callTool('oauth2-github', {});
console.log('Auth URL:', authResult.data.authUrl);

// After authentication, call OAuth-protected tools
const profile = await service.callTool('get-github-profile', {});

Using with AI SDK

import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

const tools = await service.getToolsAsAISDKTools();

const result = await generateText({
  model: anthropic('claude-3-5-sonnet-20241022'),
  prompt: 'What is the weather in San Francisco?',
  tools,
  maxSteps: 10,
});

console.log(result.text);

🚀 Advanced Features

Contexts

Contexts provide dynamic information about the service or user state:

const userContext = {
  id: 'user-preferences',
  name: 'User Preferences',
  description: 'User-specific preferences and settings',
  getContextData: async (agentInfo, extraData) => {
    // Fetch user preferences from database
    return {
      theme: 'dark',
      language: 'en',
      timezone: 'America/Los_Angeles',
    };
  },
};

const service = defineDAINService({
  contexts: [userContext],
  // ... rest of config
});

Datasources

Datasources provide queryable data to AI agents:

const productDatasource = {
  id: 'products',
  name: 'Product Catalog',
  description: 'Search and browse products',
  type: 'data' as const,
  input: z.object({
    category: z.string().optional(),
    minPrice: z.number().optional(),
    maxPrice: z.number().optional(),
  }),
  getDatasource: async (agentInfo, params, extraData) => {
    // Query database
    const products = await db.products.findMany({
      where: {
        category: params.category,
        price: {
          gte: params.minPrice,
          lte: params.maxPrice,
        },
      },
    });

    return products;
  },
};

const service = defineDAINService({
  datasources: [productDatasource],
  // ... rest of config
});

Widgets

Widgets display information in the DAIN UI:

const dashboardWidget = {
  id: 'dashboard',
  name: 'Dashboard',
  description: 'Overview dashboard',
  icon: 'dashboard',
  size: 'lg' as const,
  getWidget: async (agentInfo, extraData) => {
    const stats = await getStats(agentInfo.agentId);

    return {
      text: 'Dashboard overview',
      data: stats,
      ui: {
        type: 'dashboard',
        children: [
          { type: 'stat', label: 'Total Users', value: stats.users },
          { type: 'stat', label: 'Active Sessions', value: stats.sessions },
        ],
      },
    };
  },
};

const service = defineDAINService({
  widgets: [dashboardWidget],
  // ... rest of config
});

Long-Running Processes

For operations that take time to complete:

import { MemoryProcessStore } from '@dainprotocol/service-sdk';

const service = defineDAINService({
  processStore: new MemoryProcessStore(),
  // ... rest of config
});

// In a tool handler
const processTool = {
  id: 'start-analysis',
  name: 'Start Analysis',
  description: 'Start a long-running analysis',
  handler: async (input, agentInfo, context) => {
    const processId = generateId();

    // Start background process
    startAnalysisProcess(processId, input);

    return {
      text: 'Analysis started',
      data: { processId },
      ui: undefined,
      processes: [{
        id: processId,
        name: 'Data Analysis',
        description: 'Analyzing your data...',
        type: 'one-time',
      }],
    };
  },
};

Human-in-the-Loop Actions

Request human approval or input during tool execution:

const service = defineDAINService({
  onHumanActionResponse: async ({ process, stepId, actionId, responseText, data }) => {
    console.log('Human responded:', responseText);
    // Continue the process based on human response
  },
  // ... rest of config
});

Custom Routes

Add custom endpoints to your service:

const service = defineDAINService({
  routes: (app) => {
    app.get('/health', (c) => {
      return c.json({ status: 'healthy' });
    });

    app.post('/webhook', async (c) => {
      const body = await c.req.json();
      // Handle webhook
      return c.json({ received: true });
    });
  },
  // ... rest of config
});

Payment Integration

import { createStripePaymentIntent } from '@dainprotocol/service-sdk/payments';

const paidTool = {
  id: 'premium-feature',
  name: 'Premium Feature',
  description: 'A premium paid feature',
  pricing: { pricePerUse: 5.00, currency: 'USD' },
  handler: async (input, agentInfo) => {
    // Request payment
    const paymentIntent = await createStripePaymentIntent({
      amount: 500, // $5.00 in cents
      currency: 'usd',
      metadata: {
        agentId: agentInfo.agentId,
        toolId: 'premium-feature',
      },
    });

    return {
      text: 'Please complete payment to continue',
      data: {},
      ui: undefined,
      pleasePay: paymentIntent,
    };
  },
};

📚 API Reference

Service Configuration

interface DAINServiceConfig {
  metadata: {
    title: string;
    description: string;
    version: string;
    author: string;
    tags: string[];
    logo?: string;
  };
  identity: {
    apiKey?: string;
    publicKey?: string;
    privateKey?: string;
    agentId?: string;
    orgId?: string;
  };
  tools: ToolConfig[];
  toolboxes?: ToolboxConfig[];
  services?: ServiceConfig[];
  contexts?: ServiceContext[];
  datasources?: ServiceDatasource[];
  widgets?: ServiceWidget[];
  agents?: ServiceAgent[];
  plugins?: DainPlugin[];
  oauth2?: {
    baseUrl: string;
    tokenStore?: StorageAdapter;
    providers: Record<string, OAuth2ProviderConfig>;
  };
  processStore?: ProcessStoreAdapter;
  routes?: (app: Hono) => void;
  serverExtensions?: NodeServerExtension[];
  baseUrl?: string;
  exampleQueries?: {
    category: string;
    queries: string[];
  }[];
  getUserWidgets?: (agentInfo: AgentInfo, extraData?: any) => Promise<string[]>;
  homeUI?: string | ((agentInfo: AgentInfo, extraData?: any) => Promise<string>);
  onHumanActionResponse?: (response: {
    process: Process;
    stepId: string;
    actionId: string;
    responseText?: string;
    data?: any;
  }) => Promise<void>;
}

Tool Configuration

interface ToolConfig<TInput extends z.ZodType, TOutput extends z.ZodType> {
  id: string;
  name: string;
  description: string;
  input: TInput;
  output: TOutput;
  pricing?: {
    pricePerUse: number;
    currency: string;
  };
  interface?: string;
  suggestConfirmation?: boolean;
  suggestConfirmationUI?: (input: z.input<TInput>) => Promise<{
    success?: boolean;
    ui?: any;
  }>;
  handler: (
    input: z.input<TInput>,
    agentInfo: AgentInfo,
    context: ToolContext
  ) => Promise<{
    text: string;
    data: z.output<TOutput>;
    ui: any | undefined;
    pleasePay?: PaymentIntent;
    processes?: string[] | ProcessInfo[];
  }>;
  handleInputError?: (
    error: ZodError,
    agentInfo: AgentInfo,
    extraData?: any
  ) => Promise<ToolResponse>;
}

Runtime Methods

// Node.js
await service.startNode({ port?: number });

// Deno
await service.startDeno({ port?: number });

// Cloudflare Workers
export default { fetch: service.startWorkers() };

// Next.js
const { GET, POST } = createNextDainService(config);

📖 Examples

Complete Weather Service

See example/simpleWeatherService-node.ts

OAuth2 Integration

See example/oauth-client-example.ts

Long-Running Processes

See example/processService.ts

Using Plugins

See example/crypto-plugin-usage.ts

Next.js Integration

See examples/nextjs-app-router

🔄 Migration Guide

If you're upgrading from an older version, see OAUTH_MIGRATION.md for OAuth2 token store migration instructions.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

ISC License - see LICENSE file for details

🔗 Links

💬 Support


Built with ❤️ by the DAIN Protocol team