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

@bandofai/unido-provider-openai

v0.19.1

Published

Unido OpenAI - Provider adapter for OpenAI ChatGPT

Readme

@bandofai/unido-provider-openai

OpenAI ChatGPT adapter for Unido applications.

npm version License: MIT

Enables your Unido applications to run as custom tools in ChatGPT using OpenAI's Custom Tools (formerly GPT Actions).


Installation

pnpm add @bandofai/unido-core @bandofai/unido-provider-openai zod

Quick Start

import { createApp, textResponse } from '@bandofai/unido-core';
import { openAI } from '@bandofai/unido-provider-openai';
import { z } from 'zod';

const app = createApp({
  name: 'my-app',
  version: '1.0.0',
  providers: {
    openai: openAI({ port: 3000 })
  }
});

app.tool('greet', {
  description: 'Greet a user',
  input: z.object({
    name: z.string()
  }),
  handler: async ({ name }) => {
    return textResponse(`Hello, ${name}!`);
  }
});

await app.listen();
// Server running on http://localhost:3000

Configuration Options

openAI(options)

Creates an OpenAI provider configuration.

Options:

{
  port?: number;         // Server port (default: 3000)
  host?: string;         // Server host (default: 'localhost')
  cors?: boolean;        // Enable CORS (default: true)
  corsOrigin?: string;   // CORS origin (default: '*')
}

Examples:

// Default configuration
openAI()

// Custom port
openAI({ port: 8080 })

// Production configuration
openAI({
  port: 3000,
  host: '0.0.0.0',
  cors: true,
  corsOrigin: 'https://chat.openai.com'
})

// Restricted CORS
openAI({
  cors: true,
  corsOrigin: 'https://yourdomain.com'
})

How It Works

Architecture

ChatGPT → HTTP Request → OpenAI Adapter → Unido Core → Your Tool Handler
                                        ← Universal Response ←
         ← Formatted Response ←

The OpenAI provider:

  1. Starts HTTP Server on the configured port
  2. Implements MCP (Model Context Protocol) for ChatGPT
  3. Converts Schemas from Zod to JSON Schema
  4. Handles Requests via Server-Sent Events (SSE)
  5. Formats Responses for OpenAI's expected format

Connecting to ChatGPT

1. Start Your Server

pnpm run dev

Your server should be accessible at http://localhost:3000 (or your configured port).

2. Add to ChatGPT

  1. Open ChatGPT
  2. Click your profile → Settings
  3. Go to Custom Tools section
  4. Click Add Server
  5. Enter your server URL: http://localhost:3000
  6. Click Connect

3. Test It

Ask ChatGPT to use your tools:

  • "Greet me with my name"
  • "Calculate 5 + 3"
  • "What's the weather in London?"

Features

Server-Sent Events (SSE)

The OpenAI adapter uses SSE for real-time communication:

// Endpoint: http://localhost:3000/sse
// ChatGPT connects to this endpoint

Health Checks

Built-in health check endpoint:

curl http://localhost:3000/health
# Returns: {"status":"ok"}

MCP Protocol

Implements Model Context Protocol v2025-06-18:

  • tools/list - List available tools
  • tools/call - Execute a tool

Advanced Features

Component Responses

Return rich UI that ChatGPT can render:

import { componentResponse } from '@bandofai/unido-core';

app.tool('show_data', {
  description: 'Display data in a card',
  input: z.object({
    title: z.string(),
    description: z.string()
  }),
  handler: async ({ title, description }) => {
    return componentResponse(
      'card',
      { title, description },
      `${title}: ${description}`  // Fallback text
    );
  }
});

Register the underlying widget once when bootstrapping your app so the adapter can bundle it and expose the ui:// resource to ChatGPT:

const userCardPath = fileURLToPath(new URL('./components/UserCard.tsx', import.meta.url));

app.component({
  type: 'user-card',
  title: 'User Card',
  sourcePath: userCardPath,
  metadata: {
    openai: {
      renderHints: {
        widgetAccessible: true,
      },
    },
  },
});

On startup the adapter bundles every registered component with esbuild, converts it into an inline ui://widget/<type>.html resource, and serves it through the MCP resources/list and resources/read handlers used by ChatGPT Apps.

OpenAI Metadata

The adapter automatically adds OpenAI-specific metadata to tools:

{
  "_meta": {
    "openai/outputTemplate": "ui://widget/card.html",
    "openai/widgetAccessible": true
  }
}

Production Deployment

Environment Variables

# .env
PORT=3000
HOST=0.0.0.0
CORS_ORIGIN=https://chat.openai.com

Load in your app:

import { openAI } from '@bandofai/unido-provider-openai';

const app = createApp({
  providers: {
    openai: openAI({
      port: parseInt(process.env.PORT || '3000'),
      host: process.env.HOST || 'localhost',
      corsOrigin: process.env.CORS_ORIGIN
    })
  }
});

Docker

Create Dockerfile:

FROM node:20-alpine

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile

COPY . .
RUN pnpm run build

EXPOSE 3000

CMD ["node", "dist/index.js"]

Build and run:

docker build -t my-unido-app .
docker run -p 3000:3000 my-unido-app

Hosting Platforms

The OpenAI provider works on any platform that supports HTTP servers:

Railway

# railway.toml
[build]
builder = "NIXPACKS"

[deploy]
startCommand = "pnpm start"

Render

# render.yaml
services:
  - type: web
    name: my-unido-app
    env: node
    buildCommand: pnpm install && pnpm run build
    startCommand: pnpm start

Vercel

{
  "builds": [
    {
      "src": "dist/index.js",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "dist/index.js"
    }
  ]
}

Troubleshooting

"Cannot find module '@modelcontextprotocol/sdk'"

The SDK is a dependency. Reinstall:

pnpm install

"Port already in use"

Check what's using the port:

lsof -i :3000

Kill it or use a different port:

openAI({ port: 3001 })

ChatGPT can't connect

Check server is running:

curl http://localhost:3000/health

Common issues:

  • Firewall blocking connections
  • Using https:// instead of http:// for local dev
  • Server not bound to correct network interface

For production:

  • Ensure server is publicly accessible
  • Use proper DNS/SSL if required
  • Check CORS settings

"Tool not found" in ChatGPT

Make sure:

  1. Server is running
  2. Tool is registered before app.listen()
  3. ChatGPT connection is active (check settings)

Schema validation errors

Ensure your Zod schemas are valid:

// ❌ Wrong
input: z.object({
  age: z.string().number()  // Invalid chain
})

// ✅ Correct
input: z.object({
  age: z.number()
})

API Details

HTTP Endpoints

| Endpoint | Method | Description | |----------|--------|-------------| | / | GET | Root endpoint, returns server info | | /health | GET | Health check ({"status":"ok"}) | | /sse | GET | Server-Sent Events endpoint for MCP |

MCP Methods

| Method | Description | |--------|-------------| | tools/list | List all available tools | | tools/call | Execute a specific tool |

Response Format

Tools return responses in this format:

{
  content: [
    { type: 'text', text: 'Your response text' }
  ]
}

With components:

{
  content: [
    { type: 'text', text: 'Fallback text' }
  ],
  component: {
    type: 'weather-card',
    props: { city: 'London', temp: 18 }
  }
}

Examples

Basic Calculator

app.tool('calculate', {
  description: 'Perform arithmetic',
  input: z.object({
    op: z.enum(['add', 'sub', 'mul', 'div']),
    a: z.number(),
    b: z.number()
  }),
  handler: async ({ op, a, b }) => {
    const ops = {
      add: a + b,
      sub: a - b,
      mul: a * b,
      div: a / b
    };
    return textResponse(`Result: ${ops[op]}`);
  }
});

Weather Tool with Component

app.tool('weather', {
  description: 'Get weather for a city',
  input: z.object({
    city: z.string()
  }),
  handler: async ({ city }) => {
    const weather = await fetchWeather(city);
    return componentResponse(
      'weather-card',
      {
        city,
        temperature: weather.temp,
        condition: weather.condition,
        humidity: weather.humidity
      },
      `${city}: ${weather.temp}°C, ${weather.condition}`
    );
  }
});

Multi-Step Tool

app.tool('analyze_code', {
  description: 'Analyze code quality',
  input: z.object({
    code: z.string(),
    language: z.enum(['javascript', 'typescript', 'python'])
  }),
  handler: async ({ code, language }) => {
    // Step 1: Parse
    const ast = await parser.parse(code, language);

    // Step 2: Analyze
    const issues = await analyzer.check(ast);

    // Step 3: Format results
    return componentResponse(
      'code-analysis',
      {
        language,
        issueCount: issues.length,
        issues: issues.map(i => ({
          line: i.line,
          severity: i.severity,
          message: i.message
        }))
      },
      `Found ${issues.length} issues in ${language} code`
    );
  }
});

Development

Running Tests

cd packages/providers/openai
pnpm test

Building

pnpm run build

Type Checking

pnpm run type-check

Links


License

MIT License - see LICENSE for details.