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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@dev-fastn-ai/ucl-sdk

v2.0.1

Published

Find the best matching tools from UCL using intelligent semantic search. Get top 5 ranked tools with confidence scores and LLM-ready prompts for seamless AI agent integration.

Readme

UCL SDK

Find the best matching tools from UCL using semantic search. Get top 5 ranked tools with confidence scores and LLM-ready prompts for AI agent integration.

Quick Start

Step 1: Get Your Credentials

Before using the UCL SDK, you need to obtain your credentials from the UCL platform:

  1. Sign up/Login to the UCL platform at app.ucl.dev
  2. Navigate to SettingsAPI Credentials
  3. Copy your credentials:
    • Space ID: Your unique space identifier (looks like: 0b394afd-e5ba-4b7c-85af-a2d6639e7c04)
    • Auth Token: Your authentication token (looks like: 7c8bc9182a07e19b262495dd1e27fa21bd5029a9)

Tip: Store these credentials securely in your environment variables or configuration files.

Step 2: Installation

npm install @dev-fastn-ai/ucl-sdk

Step 3: Basic Setup

Create your first UCL integration:

import { UCL, UCLConfig } from '@dev-fastn-ai/ucl-sdk';

// Basic configuration - just your credentials
const config: UCLConfig = {
  authToken: "your-auth-token",      // From Step 1
  spaceId: "your-space-id"           // From Step 1
  // Everything else uses defaults
};

async function findTools() {
  // Initialize UCL
  const ucl = new UCL(config);
  await ucl.initialize();

  // Find tools for a user message
  const analysis = await ucl.findTools(
    [{ role: "user", content: "Send an email to [email protected]" }]
  );

  // Check if tools were found
  if (analysis.requiresTool && analysis.topMatches) {
    console.log(`Found ${analysis.topMatches.length} matching tools!`);
    
    // Show the top matches
    analysis.topMatches.forEach((match, index) => {
      const emoji = match.confidence === 'high' ? '🟢' : 
                    match.confidence === 'medium' ? '🟡' : '🔴';
      console.log(`${index + 1}. ${emoji} ${match.tool.function.name} (${match.confidence})`);
    });

    // Best tool recommendation
    console.log(`\nBest match: ${analysis.tool?.function.name}`);
  } else {
    console.log("No matching tools found");
  }
}

findTools();

LLM Integration

The UCL SDK automatically generates LLM-ready prompts for your found tools, making AI agent integration simple:

const analysis = await ucl.findTools(messages);

if (analysis.requiresTool && analysis.llmPrompt) {
  // Ready-to-use prompt for your LLM
  console.log("LLM Prompt:", analysis.llmPrompt);
  
  // Send to your favorite LLM (OpenAI, Claude, etc.)
  const llmResponse = await your_llm_client.chat({
    messages: [{ role: "user", content: analysis.llmPrompt }]
  });
  
  // The LLM will return structured tool selection and parameters
  const toolSelection = JSON.parse(llmResponse);
  console.log("Selected tool:", toolSelection.selectedTool);
  console.log("Parameters:", toolSelection.parameters);
}

What You Get with LLM Integration:

  1. Tool Selection: LLM chooses the best tool from top 5 matches
  2. Parameter Extraction: Automatically extracts parameters from conversation
  3. Missing Parameter Detection: Identifies what additional info is needed
  4. Structured Data: Get data ready for tool execution

Complete Example: Conversation Agent

We've created a complete conversation agent example that shows how to:

  • Handle user conversations naturally
  • Ask follow-up questions for missing parameters
  • Execute tools when ready
  • Maintain conversation history
  • Provide user-friendly responses
// See examples/conversation-agent.js for the full implementation
const { UCL } = require('@dev-fastn-ai/ucl-sdk');

// Simple conversation agent that executes tools
class ConversationAgent {
  constructor() {
    this.ucl = new UCL({
      authToken: "your-auth-token",
      spaceId: "your-space-id"
    });
  }
  
  async processMessage(userMessage) {
    const analysis = await this.ucl.findTools([...conversationHistory]);
    
    if (analysis.requiresTool && analysis.tool) {
      // Check for missing parameters and ask follow-up questions
      // Execute tool when all parameters are available
      return this.executeToolWithParameters(analysis.tool, userMessage);
    }
    
    return "How can I help you today?";
  }
}

Try it yourself:

# Clone and run the example
cd examples
node conversation-agent.js

Configuration Guide

Basic Configuration (Recommended)

const config: UCLConfig = {
  authToken: "your-auth-token",
  spaceId: "your-space-id"
  // Uses defaults:
  // - environment: "DRAFT" (for testing)
  // - embedding: Local Xenova model (no external API calls)
};

Advanced Configuration

const config: UCLConfig = {
  authToken: "your-auth-token",
  spaceId: "your-space-id",
  environment: "LIVE",                    // Use "DRAFT" for testing
  baseUrl: "https://custom-api.com",      // Custom API endpoint
  
  // Advanced embedding configuration
  embeddingConfig: {
    provider: "openai",                   // "xenova" (local) or "openai" (cloud)
    openaiApiKey: "sk-...",               // Required for OpenAI
    modelSize: "large",                   // "small" | "base" | "large" (xenova only)
    preloadModel: true,                   // Faster startup
    maxBatchSize: 32                      // Batch processing size
  }
};

Usage Patterns

Pattern 1: Simple Tool Discovery

async function simpleDiscovery(userMessage: string) {
  const analysis = await ucl.findTools(
    [{ role: "user", content: userMessage }],
    "tenant-123"
  );
  
  return analysis.requiresTool ? analysis.tool : null;
}

Pattern 2: Conversation-Aware Discovery

async function contextAwareDiscovery(messages: Message[]) {
  const analysis = await ucl.findTools(messages);
  
  // Filter by confidence level
  const highConfidenceTools = analysis.topMatches?.filter(
    match => match.confidence === 'high'
  );
  
  return highConfidenceTools?.length > 0 
    ? highConfidenceTools[0] 
    : analysis.topMatches?.[0];
}

Pattern 3: Multi-Step Tool Selection

async function toolSelection(messages: Message[]) {
  const analysis = await ucl.findTools(messages);
  
  if (!analysis.requiresTool) {
    return { action: "continue_conversation" };
  }
  
  if (analysis.topMatches?.length === 1) {
    return { action: "auto_select", tool: analysis.tool };
  }
  
  // Multiple options - use LLM to decide
  if (analysis.llmPrompt) {
    const llmDecision = await callYourLLM(analysis.llmPrompt);
    return { action: "llm_selected", decision: llmDecision };
  }
  
  return { action: "ask_user", options: analysis.topMatches };
}

Understanding Results

ToolAnalysisResult Structure

{
  requiresTool: true,                     // Tools were found
  tool: {                                 // Best matching tool
    function: {
      name: "send_email",
      description: "Send an email message",
      parameters: { /* tool parameters */ }
    }
  },
  topMatches: [                          // Top 5 ranked tools
    {
      tool: { /* tool object */ },
      score: 0.89,                       // Similarity score (0-1)
      confidence: "high",                // "high" | "medium" | "low"
      rank: 1                           // Ranking position
    }
  ],
  llmPrompt: "Based on the following...", // Ready-to-use LLM prompt
  toolSelectionContext: "User Intent: Send email\nTop Tools: send_email (high), send_message (medium)",
  analysisMetrics: {
    processingTime: 45,                  // Time taken (ms)
    totalToolsAnalyzed: 127,            // Number of tools analyzed
    similarityThreshold: 0.3             // Threshold used
  }
}

Confidence Levels

  • High: Score > 0.7, likely correct tool
  • Medium: Score 0.3-0.7, probably correct but verify
  • Low: Score < 0.3, uncertain match, consider alternatives

Environment Setup

Using Environment Variables (.env)

# .env file
UCL_AUTH_TOKEN=your-auth-token-here
UCL_SPACE_ID=your-space-id-here
UCL_ENVIRONMENT=DRAFT
// In your code
import dotenv from 'dotenv';
dotenv.config();

const config: UCLConfig = {
  authToken: process.env.UCL_AUTH_TOKEN!,
  spaceId: process.env.UCL_SPACE_ID!,
  environment: process.env.UCL_ENVIRONMENT as 'DRAFT' | 'LIVE'
};

TypeScript Configuration

Add to your tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

Production Deployment

Performance Optimization

const productionConfig: UCLConfig = {
  authToken: process.env.UCL_AUTH_TOKEN!,
  spaceId: process.env.UCL_SPACE_ID!,
  environment: "LIVE",
  embeddingConfig: {
    provider: "xenova",
    modelSize: "base",        // Balance speed vs accuracy
    preloadModel: true,       // Faster responses
    maxBatchSize: 64         // Higher throughput
  }
};

// Initialize once at app startup
const ucl = new UCL(productionConfig);
await ucl.initialize();

// Clear cache periodically to manage memory
setInterval(() => {
  ucl.clearCache();
}, 30 * 60 * 1000); // Every 30 minutes

Error Handling

import { UCL, ConfigurationError } from '@dev-fastn-ai/ucl-sdk';

async function toolDiscovery(messages: Message[]) {
  try {
    const analysis = await ucl.findTools(messages);
    return analysis;
    
  } catch (error) {
    if (error instanceof ConfigurationError) {
      // Configuration issues (invalid credentials, etc.)
      console.error("UCL Configuration Error:", error.message);
      throw new Error("Service configuration error");
      
    } else {
      // Network, API, or other errors
      console.error("UCL Service Error:", error.message);
      
      // Fallback behavior
      return {
        requiresTool: false,
        message: "Tool discovery temporarily unavailable"
      };
    }
  }
}

API Reference

Core Methods

| Method | Description | Returns | |--------|-------------|---------| | initialize() | Load tools and connectors | Promise<void> | | findTools(messages) | Find matching tools with LLM prompts | Promise<ToolAnalysisResult> | | getAvailableTools() | Get all tools | Tool[] | | getAvailableConnectors() | Get all connectors | Connector[] | | refreshTools() | Reload tools from API | Promise<void> | | clearCache() | Clear analysis cache | void | | isInitialized() | Check initialization status | boolean |

Type Definitions

interface Message {
  role: 'user' | 'assistant' | 'system';
  content: string;
}

interface ToolAnalysisResult {
  requiresTool: boolean;
  tool?: Tool;
  topMatches?: ToolMatch[];
  llmPrompt?: string;              // NEW: Ready-to-use LLM prompt
  toolSelectionContext?: string;   // NEW: Context for LLM
  requiresConnection?: boolean;
  connector?: Connector;
  message?: string;
  analysisMetrics?: {
    processingTime: number;
    totalToolsAnalyzed: number;
    similarityThreshold: number;
  };
}

interface ToolMatch {
  tool: Tool;
  score: number;                   // 0-1 similarity score
  confidence: 'high' | 'medium' | 'low';
  rank: number;
  relevanceMetrics: {
    nameScore: number;
    descriptionScore: number;
    combinedScore: number;
  };
}

Common Questions

Q: Which embedding provider should I use? A: Use xenova (local) for privacy and no API costs, or openai for better accuracy with API costs.

Q: What's the difference between DRAFT and LIVE environments? A: DRAFT is for testing/development with sandbox data. LIVE is for production with real connectors and tools.

Q: How many tools can I analyze at once? A: The SDK can handle hundreds of tools efficiently. Performance scales with your embedding model choice.

Q: Can I customize the similarity threshold? A: The SDK automatically adjusts thresholds for good results, but you can filter results by confidence level.

Q: How do I handle rate limits? A: The SDK includes retry logic. For high-volume usage, consider implementing your own queueing system.

License

MIT License - see the LICENSE file for details.

Support