New

Give Your Agent Access to Every Tool

Add two tools to your AI SDK agent and instantly access thousands of tools from the TPMJS registry. No configuration, no manual imports—just dynamic tool discovery and execution.

Quick Start

1

Install the packages

npm install @tpmjs/registry-search @tpmjs/registry-execute
pnpm add @tpmjs/registry-search @tpmjs/registry-execute
2

Add to your agent

import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { registrySearchTool } from '@tpmjs/registry-search';
import { registryExecuteTool } from '@tpmjs/registry-execute';

const result = streamText({
  model: openai('gpt-4.1-mini'),
  tools: {
    // Your existing tools
    weather: weatherTool,
    database: databaseTool,

    // TPMJS registry access
    registrySearch: registrySearchTool,
    registryExecute: registryExecuteTool,
  },
  system: `You have access to thousands of tools via the TPMJS registry.
Use registrySearch to find tools, then registryExecute to run them.`,
  prompt: 'Search for web scraping tools and scrape https://example.com',
});
3

That's it!

Your agent can now discover and execute any tool from the registry. Here's what happens when a user asks for something:

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

How It Works

registrySearchTool

Search the TPMJS registry to find tools for any task. Returns metadata including the toolId needed for execution.

Parameters

NameTypeRequiredDescription
querystringYesSearch query (keywords, tool names, descriptions)
categorystringNoFilter by category
limitnumberNoMax results (1-20, default 5)

Categories

web-scrapingdata-processingfile-operationscommunicationdatabaseapi-integrationimage-processingtext-analysisautomationai-mlsecuritymonitoring

Return Value

{
  "query": "web scraping",
  "matchCount": 3,
  "tools": [
    {
      "toolId": "@firecrawl/ai-sdk::scrapeTool",
      "name": "scrapeTool",
      "package": "@firecrawl/ai-sdk",
      "description": "Scrape any website into clean markdown",
      "category": "web-scraping",
      "requiredEnvVars": ["FIRECRAWL_API_KEY"],
      "healthStatus": "HEALTHY",
      "qualityScore": 0.9
    }
  ]
}

registryExecuteTool

Execute any tool from the registry by its toolId. Tools run in a secure sandbox—no local installation required.

Parameters

NameTypeRequiredDescription
toolIdstringYesTool identifier (format: package::name)
paramsobjectYesParameters to pass to the tool
envobjectNoEnvironment variables (API keys)

Example

// Execute a web search tool
const result = await registryExecuteTool.execute({
  toolId: '@exalabs/ai-sdk::webSearch',
  params: { query: 'latest AI news' },
  env: { EXA_API_KEY: 'your-api-key' },
});

// Result:
// {
//   toolId: '@exalabs/ai-sdk::webSearch',
//   executionTimeMs: 1234,
//   output: { results: [...] }
// }

Return Value

{
  "toolId": "@exalabs/ai-sdk::webSearch",
  "executionTimeMs": 1234,
  "output": { ... }
}

Environment Variables

Both packages support self-hosted registries via environment variables. This is useful for enterprise deployments or running your own tool registry.

VariableDefaultDescription
TPMJS_API_URLhttps://tpmjs.comBase URL for the registry API
TPMJS_EXECUTOR_URLhttps://executor.tpmjs.comURL for the sandbox executor

Self-Hosted Example

# Use your own TPMJS registry
export TPMJS_API_URL=https://registry.mycompany.com
export TPMJS_EXECUTOR_URL=https://executor.mycompany.com

Passing API Keys

Many tools require API keys (e.g., Firecrawl, Exa). The recommended approach is to wrap registryExecuteTool with your pre-configured keys.

Create a Wrapper (Recommended)

import { tool } from 'ai';
import { registryExecuteTool } from '@tpmjs/registry-execute';

// Pre-configure your API keys
const API_KEYS: Record<string, string> = {
  FIRECRAWL_API_KEY: process.env.FIRECRAWL_API_KEY!,
  EXA_API_KEY: process.env.EXA_API_KEY!,
};

// Create a wrapped version that auto-injects keys
export const registryExecute = tool({
  description: registryExecuteTool.description,
  parameters: registryExecuteTool.parameters,
  execute: async ({ toolId, params }) => {
    return registryExecuteTool.execute({ toolId, params, env: API_KEYS });
  },
});

Use the Wrapped Tool

import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { registrySearchTool } from '@tpmjs/registry-search';
import { registryExecute } from './tools';  // Your wrapped version

const result = streamText({
  model: openai('gpt-4.1-mini'),
  tools: {
    registrySearch: registrySearchTool,
    registryExecute,  // Keys are auto-injected
  },
  system: `You have access to the TPMJS tool registry.
Use registrySearch to find tools, then registryExecute to run them.`,
  prompt: 'Scrape https://example.com and summarize the content',
});

How It Works

  1. registrySearch returns requiredEnvVars for each tool (e.g., ["FIRECRAWL_API_KEY"])
  2. Your wrapper automatically passes all configured keys to the executor
  3. The executor injects matching keys as environment variables in the sandbox
  4. Tools without required keys work with or without the wrapper

Security

🏝️

Sandboxed Execution

All tools run in an isolated Deno runtime on Railway. They cannot access your local filesystem or environment.

🔐

API Key Isolation

API keys are passed per-request and never stored. Each execution is stateless and isolated.

Registry-Only Execution

Only tools registered in TPMJS can be executed. No arbitrary code execution is possible.

🏥

Health Monitoring

Every tool is continuously health-checked. Broken tools are flagged and filtered from search results.

The Vision

We're building the npm for AI tools. Just as npm changed how developers share JavaScript packages, TPMJS aims to do the same for AI agent tools—a universal ecosystem where agents discover and use tools on-demand.

The registrySearch and registryExecute tools are just the beginning. Here's what's coming:

Coming Soon

Collections

Pre-configured tool bundles for specific domains. Think of them as “skill packs” for your AI agent.

// Future API concept
const tools = await tpmjs.loadCollection('web-scraping');
// Includes: scrapeTool, crawlTool, extractTool, searchTool...

const tools = await tpmjs.loadCollection('data-analysis');
// Includes: csvParser, jsonTransform, statistics, plotting...

// Or create your own private collections
const tools = await tpmjs.loadCollection('my-company/internal-tools');
Planned

API Keys & Rate Limiting

Personal API keys for authentication, usage tracking, and rate limiting. Enterprise features for teams including usage analytics and billing.

Planned

Tool Versioning

Pin specific tool versions in your agent configuration. Automatic compatibility checking and migration guides when tools update.

Planned

Private Registries

Run your own TPMJS instance for internal tools. Connect multiple registries (public + private) in a single agent. Enterprise SSO and access controls.

Planned

Streaming Execution

Stream tool outputs for long-running operations. Real-time progress updates and partial results for better UX.

MCP Server Integration

TPMJS supports the Model Context Protocol (MCP), allowing you to use your tool collections directly in Claude Desktop, Cursor, VS Code, and other MCP-compatible clients.

1

Create a Collection

Sign in to tpmjs.com and create a collection of tools. Add the tools you want your agent to have access to, and configure any required API keys.

2

Get Your MCP URL

Your collection has a unique MCP endpoint URL:

https://tpmjs.com/api/mcp/{username}/{collection-slug}/http

Replace {username} with your username and {collection-slug} with your collection's slug.

3

Configure Your Client

Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "tpmjs": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://tpmjs.com/api/mcp/username/my-tools/http",
        "--header",
        "Authorization: Bearer YOUR_API_KEY"
      ]
    }
  }
}

Cursor / VS Code

Add to your .cursor/mcp.json or VS Code MCP settings:

{
  "mcpServers": {
    "tpmjs": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://tpmjs.com/api/mcp/username/my-tools/http",
        "--header",
        "Authorization: Bearer YOUR_API_KEY"
      ]
    }
  }
}
4

Get an API Key

Generate an API key from your account settings. API keys authenticate your MCP requests and enable access to your private collections.

REST API Reference

For advanced integrations, you can use the TPMJS REST API directly. All endpoints are available at https://tpmjs.com/api.

GET

/api/tools

List all tools with filtering, sorting, and pagination.

Query Parameters

ParameterTypeDescription
qstringSearch query (package name, description)
categorystringFilter by category
officialbooleanFilter by official status
limitnumberResults per page (1-1000, default 20)
offsetnumberPagination offset (default 0)

Example

curl "https://tpmjs.com/api/tools?category=web-scraping&limit=10"
GET

/api/tools/search

Semantic search using BM25 algorithm. Better for natural language queries.

Query Parameters

ParameterTypeDescription
qstringSearch query (natural language)
categorystringFilter by category
limitnumberMax results (1-100, default 10)

Example

curl "https://tpmjs.com/api/tools/search?q=scrape%20website%20to%20markdown"

Response

{
  "success": true,
  "query": "scrape website to markdown",
  "results": {
    "total": 5,
    "tools": [
      {
        "id": "clx...",
        "name": "scrapeTool",
        "description": "Scrape any website into clean markdown",
        "package": {
          "npmPackageName": "@firecrawl/ai-sdk",
          "category": "web-scraping",
          "env": ["FIRECRAWL_API_KEY"]
        }
      }
    ]
  }
}
POST

/api/tools/execute/{package}/{tool}

Execute a tool in the secure sandbox. Returns the tool output.

Request Body

{
  "params": {
    "url": "https://example.com"
  },
  "env": {
    "FIRECRAWL_API_KEY": "your-api-key"
  }
}

Example

curl -X POST "https://tpmjs.com/api/tools/execute/@firecrawl/ai-sdk/scrapeTool" \
  -H "Content-Type: application/json" \
  -d '{
    "params": { "url": "https://example.com" },
    "env": { "FIRECRAWL_API_KEY": "your-key" }
  }'

Response

{
  "success": true,
  "result": {
    "markdown": "# Example Domain\n\nThis domain is for use...",
    "metadata": {
      "title": "Example Domain",
      "url": "https://example.com"
    }
  },
  "executionTimeMs": 1234
}

Building an Agent Like Omega

Omega is our flagship AI agent that demonstrates dynamic tool discovery at scale. Here's how to build something similar.

Architecture Overview

Omega uses a two-tier tool discovery pattern:

  1. Automatic discovery — Every message triggers a BM25 search to find relevant tools
  2. Agent-driven search — The agent can explicitly search for more tools using registrySearchTool

Complete Implementation

import { streamText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { registrySearchTool } from '@tpmjs/registry-search';
import { registryExecuteTool } from '@tpmjs/registry-execute';

// Pre-configure API keys for tool execution
const API_KEYS: Record<string, string> = {
  FIRECRAWL_API_KEY: process.env.FIRECRAWL_API_KEY!,
  EXA_API_KEY: process.env.EXA_API_KEY!,
  OPENAI_API_KEY: process.env.OPENAI_API_KEY!,
};

// Wrapped execute tool with pre-configured keys
const registryExecute = tool({
  description: registryExecuteTool.description,
  parameters: registryExecuteTool.parameters,
  execute: async ({ toolId, params }) => {
    return registryExecuteTool.execute({ toolId, params, env: API_KEYS });
  },
});

// System prompt for Omega-like behavior
const SYSTEM_PROMPT = `You are an AI assistant with access to thousands of tools via the TPMJS registry.

## Available Tools
- registrySearch: Search the registry to find tools for any task
- registryExecute: Execute any tool by its toolId

## Workflow
1. When given a task, first search for relevant tools
2. Review the results - each tool has: toolId, name, description, requiredEnvVars
3. Execute tools with appropriate parameters
4. Synthesize results into a helpful response

## Best Practices
- Search first when unsure what tools exist
- Execute tools to get real results (not just descriptions)
- Handle errors gracefully - suggest alternatives if a tool fails
- Be efficient - don't search repeatedly for the same thing`;

// Auto-discover tools based on user message
async function discoverTools(message: string) {
  const response = await fetch(
    `https://tpmjs.com/api/tools/search?q=${encodeURIComponent(message)}&limit=10`
  );
  const data = await response.json();
  return data.results?.tools || [];
}

// Main agent function
async function runAgent(userMessage: string) {
  // Step 1: Auto-discover relevant tools
  const discoveredTools = await discoverTools(userMessage);
  console.log(`Found ${discoveredTools.length} relevant tools`);

  // Step 2: Create dynamic tool context for the prompt
  const toolContext = discoveredTools.length > 0
    ? `\n\n## Pre-discovered Tools\nBased on your request, these tools may be helpful:\n${
        discoveredTools.map((t: { toolId: string; description: string }) =>
          `- ${t.toolId}: ${t.description}`
        ).join('\n')
      }`
    : '';

  // Step 3: Run the agent with tool access
  const result = await streamText({
    model: openai('gpt-4.1-mini'),
    tools: {
      registrySearch: registrySearchTool,
      registryExecute,
    },
    maxSteps: 10,
    system: SYSTEM_PROMPT + toolContext,
    prompt: userMessage,
  });

  // Step 4: Stream the response
  for await (const chunk of result.textStream) {
    process.stdout.write(chunk);
  }

  return result;
}

// Usage
await runAgent('Scrape https://example.com and summarize the content');

Streaming with Server-Sent Events

For real-time UI updates, stream tool execution status via SSE:

// API Route: POST /api/chat
export async function POST(request: Request) {
  const { message } = await request.json();

  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    async start(controller) {
      // Emit tool discovery event
      const tools = await discoverTools(message);
      controller.enqueue(encoder.encode(
        `event: tools.discovered\ndata: ${JSON.stringify({ tools })}\n\n`
      ));

      // Run agent and stream events
      const result = await streamText({
        model: openai('gpt-4.1-mini'),
        tools: { registrySearch: registrySearchTool, registryExecute },
        maxSteps: 10,
        prompt: message,
        onStepFinish: ({ stepType, toolCalls, toolResults }) => {
          if (stepType === 'tool-result') {
            controller.enqueue(encoder.encode(
              `event: tool.completed\ndata: ${JSON.stringify({ toolCalls, toolResults })}\n\n`
            ));
          }
        },
      });

      // Stream text chunks
      for await (const chunk of result.textStream) {
        controller.enqueue(encoder.encode(
          `event: message.delta\ndata: ${JSON.stringify({ content: chunk })}\n\n`
        ));
      }

      controller.enqueue(encoder.encode(`event: done\ndata: {}\n\n`));
      controller.close();
    },
  });

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    },
  });
}

Ready to Get Started?

Give your AI agent access to thousands of tools in minutes.