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

@rainfall-devkit/sdk

v0.2.10

Published

Official SDK for Rainfall API - 200+ tools for building AI-powered applications

Downloads

1,104

Readme

Rainfall SDK

Official SDK for the Rainfall API - 200+ tools for building AI-powered applications. Utilities to leverage the backend tools we use for our own applications like Harmonic to bootstrap your own projects.

npm version License: MIT

Features

  • 200+ Tools - GitHub, Notion, Linear, Slack, Figma, Stripe, and more
  • Semantic Memory - Store and recall information with vector search
  • Web Search - Exa and Perplexity integration
  • AI Tools - Embeddings, image generation, OCR, vision, chat
  • Data Processing - CSV, scripts, similarity search
  • Developer Friendly - TypeScript, retry logic, error handling
  • MCP Support - Use with Claude, Cursor, and other AI assistants

Installation

npm install @rainfall-devkit/sdk
# or
yarn add @rainfall-devkit/sdk
# or
bun add @rainfall-devkit/sdk

Quick Start

import { Rainfall } from '@rainfall-devkit/sdk';

const rainfall = new Rainfall({
  apiKey: process.env.RAINFALL_API_KEY!
});

// Search the web
const results = await rainfall.web.search.exa({
  query: 'latest AI breakthroughs'
});

// Create a GitHub issue
await rainfall.integrations.github.issues.create({
  owner: 'facebook',
  repo: 'react',
  title: 'Bug: Something is broken',
  body: 'Detailed description...'
});

// Store and recall memories
await rainfall.memory.create({
  content: 'User prefers dark mode',
  keywords: ['preference', 'ui']
});

const memories = await rainfall.memory.recall({
  query: 'user preferences',
  topK: 5
});

CLI

Install globally to use the CLI:

bun add -g @rainfall-devkit/sdk

Authentication

rainfall auth login <your-api-key>

List Tools

rainfall tools list

Execute a Tool

rainfall run exa-web-search -p '{"query": "AI news"}'

Piping Support

echo '{"query": "hello"}' | rainfall run exa-web-search

Namespaces

Integrations

// GitHub
await rainfall.integrations.github.issues.create({ owner, repo, title });
await rainfall.integrations.github.repos.get({ owner, repo });

// Notion
await rainfall.integrations.notion.pages.create({ parent, properties });
await rainfall.integrations.notion.databases.query({ databaseId });

// Linear
await rainfall.integrations.linear.issues.create({ title, teamId });
await rainfall.integrations.linear.teams.list();

// Slack
await rainfall.integrations.slack.messages.send({ channelId, text });
await rainfall.integrations.slack.channels.list();

// Figma
await rainfall.integrations.figma.files.get({ fileKey });
await rainfall.integrations.figma.files.getImages({ fileKey, nodeIds });

// Stripe
await rainfall.integrations.stripe.customers.create({ email });
await rainfall.integrations.stripe.paymentIntents.create({ amount, currency });

Memory

// Create memory
await rainfall.memory.create({
  content: 'Important information',
  keywords: ['key', 'info'],
  metadata: { source: 'user' }
});

// Recall by similarity
const memories = await rainfall.memory.recall({
  query: 'important information',
  topK: 10
});

// CRUD operations
await rainfall.memory.get({ memoryId: '...' });
await rainfall.memory.update({ memoryId: '...', content: 'Updated' });
await rainfall.memory.delete({ memoryId: '...' });
await rainfall.memory.list();

Articles

// Search news
const articles = await rainfall.articles.search({
  query: 'artificial intelligence',
  limit: 10
});

// Create from URL
const article = await rainfall.articles.createFromUrl({ url });

// Summarize
const summary = await rainfall.articles.summarize({
  text: article.content,
  length: 'medium'
});

// Extract topics
const topics = await rainfall.articles.extractTopics({ text });

Web

// Search
const exaResults = await rainfall.web.search.exa({ query: '...' });
const perplexityResults = await rainfall.web.search.perplexity({ query: '...' });

// Fetch and convert
const html = await rainfall.web.fetch({ url: 'https://example.com' });
const markdown = await rainfall.web.htmlToMarkdown({ html });

// Extract elements
const links = await rainfall.web.extractHtml({
  html,
  selector: 'a[href]'
});

AI

// Embeddings
const docEmbedding = await rainfall.ai.embeddings.document({ text });
const queryEmbedding = await rainfall.ai.embeddings.query({ text });
const imageEmbedding = await rainfall.ai.embeddings.image({ imageBase64 });

// Image generation
const image = await rainfall.ai.image.generate({
  prompt: 'A serene mountain landscape',
  size: '1024x1024'
});

// OCR and Vision
const text = await rainfall.ai.ocr({ imageBase64 });
const analysis = await rainfall.ai.vision({
  imageBase64,
  prompt: 'Describe this image'
});

// Chat and completion
const response = await rainfall.ai.chat({
  messages: [{ role: 'user', content: 'Hello!' }],
  model: 'grok-2'
});

const completion = await rainfall.ai.complete({
  prompt: 'The quick brown',
  suffix: 'jumps over the lazy dog'
});

// Classification and segmentation
const classification = await rainfall.ai.classify({
  text: 'This is great!',
  labels: ['positive', 'negative', 'neutral']
});

const segments = await rainfall.ai.segment({
  text: longText,
  maxLength: 500
});

Data

// CSV operations
const results = await rainfall.data.csv.query({
  sql: 'SELECT * FROM data WHERE value > 100'
});

await rainfall.data.csv.convert({
  data: csvData,
  fromFormat: 'csv',
  toFormat: 'json'
});

// Scripts
await rainfall.data.scripts.create({
  name: 'process-data',
  code: 'return input.map(x => x * 2);',
  language: 'javascript'
});

const result = await rainfall.data.scripts.execute({
  name: 'process-data',
  params: { input: [1, 2, 3] }
});

await rainfall.data.scripts.list();
await rainfall.data.scripts.update({ name, code });
await rainfall.data.scripts.delete({ name });

// Similarity search
const matches = await rainfall.data.similarity.search({
  query: embedding,
  embeddings: corpus,
  topK: 5
});

Utils

// Mermaid diagrams
const diagram = await rainfall.utils.mermaid({
  diagram: `
    graph TD
      A[Start] --> B{Decision}
      B -->|Yes| C[Action 1]
      B -->|No| D[Action 2]
  `
});

// Document conversion
const pdf = await rainfall.utils.documentConvert({
  document: markdownContent,
  mimeType: 'text/markdown',
  format: 'pdf'
});

// Regex
const matches = await rainfall.utils.regex.match({
  text: 'Hello 123 world',
  pattern: '\\d+',
  flags: 'g'
});

const replaced = await rainfall.utils.regex.replace({
  text: 'Hello world',
  pattern: 'world',
  replacement: 'universe'
});

// JSON extraction
const json = await rainfall.utils.jsonExtract({
  text: 'Data: {"key": "value"}'
});

// Digest
const hash = await rainfall.utils.digest({ data: 'text to hash' });

// Monte Carlo simulation
const simulation = await rainfall.utils.monteCarlo({
  iterations: 10000,
  formula: 'price * (1 + return)',
  variables: {
    return: { mean: 0.08, stdDev: 0.16 }
  }
});

Error Handling

import { Rainfall, RateLimitError, AuthenticationError, NotFoundError } from '@rainfall/sdk';

try {
  await rainfall.integrations.github.issues.get({ owner, repo, issue_number: 999999 });
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}s`);
    console.log(`Remaining: ${error.remaining}/${error.limit}`);
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof NotFoundError) {
    console.log(`Resource not found: ${error.message}`);
  } else {
    console.log('Unexpected error:', error);
  }
}

Daemon

Run a local daemon with WebSocket (MCP) and OpenAI-compatible API endpoints:

# Start the daemon
rainfall daemon start

# Start with custom ports
rainfall daemon start --port 8765 --openai-port 8787

# Check status
rainfall daemon status

# Stop the daemon
rainfall daemon stop

Using the Daemon

WebSocket (MCP) Endpoint: ws://localhost:8765

OpenAI-compatible API: http://localhost:8787/v1/chat/completions

# Test the OpenAI-compatible endpoint
curl http://localhost:8787/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "rainfall",
    "messages": [{"role": "user", "content": "hello"}]
  }'

Programmatic Usage

import { RainfallDaemon } from '@rainfall-devkit/sdk/daemon';

const daemon = new RainfallDaemon({
  port: 8765,        // WebSocket/MCP port
  openaiPort: 8787,  // OpenAI-compatible API port
});

await daemon.start();
// Daemon is running with all Rainfall tools loaded

// Later...
await daemon.stop();

MCP Server

Use Rainfall with Claude, Cursor, and other MCP-compatible assistants:

import { createRainfallMCPServer } from '@rainfall/sdk/mcp';

const server = createRainfallMCPServer({
  apiKey: process.env.RAINFALL_API_KEY!
});

await server.start();

Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "rainfall": {
      "command": "npx",
      "args": ["-y", "@rainfall/sdk/mcp"],
      "env": {
        "RAINFALL_API_KEY": "your-api-key"
      }
    }
  }
}

Configuration

const rainfall = new Rainfall({
  apiKey: 'your-api-key',
  baseUrl: 'https://custom-endpoint.com/v1', // Optional
  timeout: 60000,  // Request timeout in ms (default: 30000)
  retries: 5,      // Number of retries (default: 3)
  retryDelay: 2000 // Initial retry delay in ms (default: 1000)
});

Rate Limiting

The SDK automatically handles rate limiting with exponential backoff:

// Check rate limit info
const info = rainfall.getRateLimitInfo();
console.log(info);
// { limit: 1000, remaining: 950, resetAt: Date }

Examples

GitHub to Notion Sync

// Get GitHub issues
const issues = await rainfall.integrations.github.issues.list({
  owner: 'myorg',
  repo: 'myrepo',
  state: 'open'
});

// Create Notion pages for each issue
for (const issue of issues) {
  await rainfall.integrations.notion.pages.create({
    parent: { database_id: 'my-database-id' },
    properties: {
      Name: { title: [{ text: { content: issue.title } }] },
      'Issue URL': { url: issue.html_url },
      Status: { select: { name: issue.state } }
    }
  });
}

PDF to Estimate

// Fetch PDF
const response = await fetch('https://example.com/quote.pdf');
const buffer = await response.arrayBuffer();
const base64 = Buffer.from(buffer).toString('base64');

// Extract text with OCR
const { text } = await rainfall.ai.ocr({ imageBase64: base64 });

// Extract structured data
const estimate = await rainfall.ai.complete({
  prompt: `Extract line items from this quote:\n\n${text}\n\nJSON format:`,
  suffix: ''
});

console.log(JSON.parse(estimate));

Memory Agent

// Store conversation context
await rainfall.memory.create({
  content: `User asked about pricing. Explained $9/mo for 100k calls.`,
  keywords: ['pricing', 'conversation'],
  metadata: { userId: 'user-123', timestamp: Date.now() }
});

// Later, recall relevant context
const context = await rainfall.memory.recall({
  query: 'What did I tell the user about pricing?',
  topK: 3
});

// Use context in response
const response = await rainfall.ai.chat({
  messages: [
    { role: 'system', content: 'Previous context: ' + JSON.stringify(context) },
    { role: 'user', content: 'What was our pricing again?' }
  ]
});

License

MIT © Pragma Digital