tpmjs/docs/REGISTRY_SDK_DESIGN.md
Ajax Davis fada29ecbe feat: add npm-like features to tool detail page and update keyword to tpmjs
- Add download trend sparkline chart showing 30-day download history
- Add bundle size component with minified/gzipped sizes via bundlephobia proxy
- Add more install commands: yarn, bun, deno (in addition to npm, pnpm)
- Change discovery keyword from "tpmjs-tool" to "tpmjs" across entire codebase
- Update sync endpoints to use new keyword
- Update all documentation and package.json files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-29 21:30:42 +10:00

13 KiB

TPMJS Registry SDK Design

This document describes the design for @tpmjs/registrySearch and @tpmjs/registryExecute packages that allow any AI SDK v4+ project to access the entire TPMJS tool registry.

Overview

The goal is to let developers add two tools to their existing AI SDK agent and instantly gain access to thousands of tools from the TPMJS registry:

import { weatherTool } from './tools/weather';
import { registrySearchTool } from '@tpmjs/registrySearch';
import { registryExecuteTool } from '@tpmjs/registryExecute';

export const agent = new Agent({
  model: 'anthropic/claude-sonnet-4-20250514',
  instructions: 'You are a helpful assistant with access to many tools.',
  tools: {
    weather: weatherTool,              // Their own tools
    registrySearch: registrySearchTool,    // Search TPMJS registry
    registryExecute: registryExecuteTool,  // Execute any registry tool
  },
});

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      User's Agent                                │
│  ┌─────────────┐  ┌────────────────┐  ┌─────────────────────┐   │
│  │ weatherTool │  │ registrySearch │  │   registryExecute   │   │
│  └─────────────┘  └───────┬────────┘  └──────────┬──────────┘   │
└───────────────────────────┼──────────────────────┼──────────────┘
                            │                      │
                            ▼                      ▼
                  ┌─────────────────┐   ┌─────────────────────────┐
                  │  TPMJS Search   │   │   Railway Executor      │
                  │     API         │   │   (Sandboxed Deno)      │
                  └─────────────────┘   └─────────────────────────┘
                            │                      │
                            ▼                      ▼
                  ┌─────────────────┐   ┌─────────────────────────┐
                  │  Tool Registry  │   │   esm.sh (CDN)          │
                  │   (Postgres)    │   │   Dynamic imports       │
                  └─────────────────┘   └─────────────────────────┘

Package Design

Two separate packages so users can install only what they need:

@tpmjs/registrySearch   - Search the TPMJS registry for tools
@tpmjs/registryExecute  - Execute any tool from the registry

Environment Variables

Both packages support self-hosted registries via environment variables:

Variable Default Description
TPMJS_API_URL https://tpmjs.com Base URL for the registry API
TPMJS_EXECUTOR_URL https://executor.tpmjs.com URL for the sandbox executor

This allows users to run their own TPMJS registry and executor.

Tool Definitions

1. @tpmjs/registrySearch

import { tool } from 'ai';
import { z } from 'zod';

const TPMJS_API_URL = process.env.TPMJS_API_URL || 'https://tpmjs.com';

export const registrySearchTool = tool({
  description: `Search the TPMJS tool registry to discover available tools.
Use this when you need to find a tool for a specific task.
Returns tool metadata including name, description, required env vars, and how to execute it.`,

  parameters: z.object({
    query: z.string().describe('Search query describing what you want to do'),
    category: z.enum([
      'search',
      'code',
      'data',
      'media',
      'communication',
      'productivity',
      'finance',
      'ai-ml',
      'devops',
      'other'
    ]).optional().describe('Filter by category'),
    limit: z.number().min(1).max(20).default(5).describe('Max results to return'),
  }),

  execute: async ({ query, category, limit }) => {
    const params = new URLSearchParams({
      q: query,
      limit: String(limit),
      ...(category && { category }),
    });

    const response = await fetch(`${TPMJS_API_URL}/api/tools/search?${params}`);
    const data = await response.json();

    return {
      query,
      matchCount: data.data.length,
      tools: data.data.map((tool: any) => ({
        // Unique identifier for registryExecuteTool
        toolId: `${tool.package.npmPackageName}::${tool.exportName}`,

        // Human-readable info
        name: tool.exportName,
        package: tool.package.npmPackageName,
        description: tool.description,
        category: tool.category,

        // Execution requirements
        requiredEnvVars: tool.env?.filter((e: any) => e.required).map((e: any) => e.name) || [],

        // Quality indicators
        healthStatus: tool.executionHealth,
        qualityScore: tool.qualityScore,
      })),
    };
  },
});

2. @tpmjs/registryExecute

import { tool } from 'ai';
import { z } from 'zod';

const TPMJS_API_URL = process.env.TPMJS_API_URL || 'https://tpmjs.com';
const TPMJS_EXECUTOR_URL = process.env.TPMJS_EXECUTOR_URL || 'https://executor.tpmjs.com';

export const registryExecuteTool = tool({
  description: `Execute a tool from the TPMJS registry.
Use registrySearchTool first to find the toolId, then call this with the toolId and parameters.
The tool runs in a secure sandbox - you don't need to install anything.`,

  parameters: z.object({
    toolId: z.string().describe('Tool identifier from registrySearchTool (format: "package::exportName")'),
    params: z.record(z.any()).describe('Parameters to pass to the tool'),
    env: z.record(z.string()).optional().describe('Environment variables (API keys) if required'),
  }),

  execute: async ({ toolId, params, env }) => {
    const [packageName, exportName] = toolId.split('::');

    if (!packageName || !exportName) {
      throw new Error(`Invalid toolId format. Expected "package::exportName", got "${toolId}"`);
    }

    // Get tool metadata to find version and importUrl
    const metaResponse = await fetch(
      `${TPMJS_API_URL}/api/tools?package=${encodeURIComponent(packageName)}&export=${encodeURIComponent(exportName)}`
    );
    const metaData = await metaResponse.json();
    const toolMeta = metaData.data?.[0];

    if (!toolMeta) {
      throw new Error(`Tool not found: ${toolId}`);
    }

    // Execute via sandbox executor
    const response = await fetch(`${TPMJS_EXECUTOR_URL}/execute-tool`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        packageName,
        exportName,
        version: toolMeta.package.npmVersion,
        importUrl: toolMeta.importUrl || `https://esm.sh/${packageName}@${toolMeta.package.npmVersion}`,
        params,
        env: env || {},
      }),
    });

    const result = await response.json();

    if (!result.success) {
      throw new Error(result.error || 'Tool execution failed');
    }

    return {
      toolId,
      executionTimeMs: result.executionTimeMs,
      output: result.output,
    };
  },
});

Usage Examples

Basic Usage

import { Agent } from 'ai';
import { registrySearchTool } from '@tpmjs/registrySearch';
import { registryExecuteTool } from '@tpmjs/registryExecute';

const agent = new Agent({
  model: 'anthropic/claude-sonnet-4-20250514',
  tools: {
    registrySearch: registrySearchTool,
    registryExecute: registryExecuteTool,
  },
});

// User: "Search the web for latest AI news"
// Agent:
//   1. Calls registrySearch({ query: "web search" })
//   2. Finds @exalabs/ai-sdk::webSearch
//   3. Calls registryExecute({
//        toolId: "@exalabs/ai-sdk::webSearch",
//        params: { query: "latest AI news" },
//        env: { EXA_API_KEY: "..." }
//      })
//   4. Returns results to user

With Their Own Tools

import { Agent } from 'ai';
import { registrySearchTool } from '@tpmjs/registrySearch';
import { registryExecuteTool } from '@tpmjs/registryExecute';
import { weatherTool } from './tools/weather';
import { databaseTool } from './tools/database';

const agent = new Agent({
  model: 'anthropic/claude-sonnet-4-20250514',
  instructions: `You are a helpful assistant.

For common tasks, use the built-in tools (weather, database).
For anything else, search the TPMJS registry to find appropriate tools.`,

  tools: {
    // Their custom tools
    weather: weatherTool,
    database: databaseTool,

    // TPMJS registry access
    registrySearch: registrySearchTool,
    registryExecute: registryExecuteTool,
  },
});

Self-Hosted Registry

// Set environment variables for your own registry
// TPMJS_API_URL=https://registry.mycompany.com
// TPMJS_EXECUTOR_URL=https://executor.mycompany.com

import { registrySearchTool } from '@tpmjs/registrySearch';
import { registryExecuteTool } from '@tpmjs/registryExecute';

// Tools will automatically use your self-hosted URLs
const agent = new Agent({
  model: 'anthropic/claude-sonnet-4-20250514',
  tools: {
    registrySearch: registrySearchTool,
    registryExecute: registryExecuteTool,
  },
});

API Endpoints Required

1. Search API (existing, may need updates)

GET https://tpmjs.com/api/tools/search?q=web+search&limit=5&category=search

Response:

{
  "success": true,
  "data": [
    {
      "id": "...",
      "exportName": "webSearch",
      "description": "Search the web...",
      "category": "search",
      "executionHealth": "HEALTHY",
      "qualityScore": 0.9,
      "env": [
        { "name": "EXA_API_KEY", "required": true }
      ],
      "package": {
        "npmPackageName": "@exalabs/ai-sdk",
        "npmVersion": "1.0.5"
      },
      "importUrl": "https://esm.sh/@exalabs/ai-sdk@1.0.5"
    }
  ]
}

2. Execute API (existing Railway executor)

POST https://executor.tpmjs.com/execute-tool

Request:

{
  "packageName": "@exalabs/ai-sdk",
  "exportName": "webSearch",
  "version": "1.0.5",
  "importUrl": "https://esm.sh/@exalabs/ai-sdk@1.0.5",
  "params": { "query": "latest AI news" },
  "env": { "EXA_API_KEY": "..." }
}

Response:

{
  "success": true,
  "output": { ... },
  "executionTimeMs": 1234
}

Package Structure

@tpmjs/registrySearch

packages/registrySearch/
├── package.json
├── tsconfig.json
├── src/
│   └── index.ts           # Exports registrySearchTool
└── README.md

@tpmjs/registryExecute

packages/registryExecute/
├── package.json
├── tsconfig.json
├── src/
│   └── index.ts           # Exports registryExecuteTool
└── README.md

package.json (@tpmjs/registrySearch)

{
  "name": "@tpmjs/registrySearch",
  "version": "0.1.0",
  "description": "Search the TPMJS tool registry from any AI SDK agent",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "keywords": ["tpmjs", "tpmjs", "ai-sdk", "vercel-ai", "tools", "registry", "search"],
  "peerDependencies": {
    "ai": "^4.0.0",
    "zod": "^3.0.0"
  }
}

package.json (@tpmjs/registryExecute)

{
  "name": "@tpmjs/registryExecute",
  "version": "0.1.0",
  "description": "Execute tools from the TPMJS registry in any AI SDK agent",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "keywords": ["tpmjs", "tpmjs", "ai-sdk", "vercel-ai", "tools", "registry", "execute"],
  "peerDependencies": {
    "ai": "^4.0.0",
    "zod": "^3.0.0"
  }
}

Security Considerations

  1. Sandboxed Execution: All tools run in the Railway Deno sandbox, not in the user's process
  2. No Code Injection: Users can't execute arbitrary code, only registered tools
  3. API Key Isolation: Keys are passed per-request, not stored
  4. Health Checks: Only HEALTHY tools should be returned in search results by default

Next Steps

  1. Create packages/registrySearch directory
  2. Create packages/registryExecute directory
  3. Implement registrySearchTool
  4. Implement registryExecuteTool
  5. Add search API endpoint if not exists
  6. Write README with examples for each package
  7. Publish to npm
  8. Create demo agent using the SDK

Future Enhancements

  1. Tool Recommendations: Based on conversation context, proactively suggest tools
  2. Tool Chaining: Pre-built workflows combining multiple tools
  3. Local Caching: Cache tool metadata for faster searches
  4. Type Generation: Generate TypeScript types for popular tools
  5. Usage Analytics: Track which tools are used most (anonymized)
  6. Rate Limiting: Add rate limiting when needed