feat: add @tpmjs/registrySearch and @tpmjs/registryExecute SDK packages
- Create @tpmjs/registrySearch package for searching tool registry - Create @tpmjs/registryExecute package for executing tools via sandbox - Support self-hosted registries via TPMJS_API_URL and TPMJS_EXECUTOR_URL env vars - Add /sdk documentation page with usage examples and architecture - Add SDK link to navigation menu - Include design doc for registry SDK architecture 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e85bbe8638
commit
ee825aa97c
12 changed files with 1626 additions and 4 deletions
558
apps/web/src/app/sdk/page.tsx
Normal file
558
apps/web/src/app/sdk/page.tsx
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
import { Button } from '@tpmjs/ui/Button/Button';
|
||||
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
||||
import { Container } from '@tpmjs/ui/Container/Container';
|
||||
import Link from 'next/link';
|
||||
import { AppHeader } from '~/components/AppHeader';
|
||||
|
||||
export const metadata = {
|
||||
title: 'SDK - Registry Tools | TPMJS',
|
||||
description:
|
||||
'Add two tools to your AI agent and instantly access thousands of tools from the TPMJS registry',
|
||||
};
|
||||
|
||||
export default function SDKPage(): React.ReactElement {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
<AppHeader />
|
||||
|
||||
<main className="flex-1 py-16">
|
||||
<Container size="lg" padding="lg">
|
||||
{/* Hero */}
|
||||
<div className="text-center mb-16">
|
||||
<div className="inline-flex items-center gap-2 mb-4">
|
||||
<span className="px-3 py-1 text-sm font-semibold bg-primary/10 text-primary rounded-full">
|
||||
New
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">
|
||||
Give Your Agent Access to Every Tool
|
||||
</h1>
|
||||
<p className="text-xl text-foreground-secondary max-w-3xl mx-auto">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Start */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">Quick Start</h2>
|
||||
<div className="space-y-6">
|
||||
{/* Install */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="flex items-center justify-center w-8 h-8 rounded-full bg-primary text-primary-foreground font-bold">
|
||||
1
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">Install the packages</h3>
|
||||
</div>
|
||||
<CodeBlock
|
||||
language="bash"
|
||||
code={`npm install @tpmjs/registrySearch @tpmjs/registryExecute
|
||||
# or
|
||||
pnpm add @tpmjs/registrySearch @tpmjs/registryExecute`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Add to agent */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="flex items-center justify-center w-8 h-8 rounded-full bg-primary text-primary-foreground font-bold">
|
||||
2
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">Add to your agent</h3>
|
||||
</div>
|
||||
<CodeBlock
|
||||
language="typescript"
|
||||
code={`import { Agent } from 'ai';
|
||||
import { registrySearchTool } from '@tpmjs/registrySearch';
|
||||
import { registryExecuteTool } from '@tpmjs/registryExecute';
|
||||
|
||||
const agent = new Agent({
|
||||
model: 'anthropic/claude-sonnet-4-20250514',
|
||||
instructions: \`You have access to thousands of tools via the TPMJS registry.
|
||||
Use registrySearch to find tools, then registryExecute to run them.\`,
|
||||
tools: {
|
||||
// Your existing tools
|
||||
weather: weatherTool,
|
||||
database: databaseTool,
|
||||
|
||||
// TPMJS registry access
|
||||
registrySearch: registrySearchTool,
|
||||
registryExecute: registryExecuteTool,
|
||||
},
|
||||
});`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* That's it */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="flex items-center justify-center w-8 h-8 rounded-full bg-primary text-primary-foreground font-bold">
|
||||
3
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">That's it!</h3>
|
||||
</div>
|
||||
<p className="text-foreground-secondary mb-4">
|
||||
Your agent can now discover and execute any tool from the registry. Here's
|
||||
what happens when a user asks for something:
|
||||
</p>
|
||||
<div className="p-4 bg-background rounded border border-border font-mono text-sm">
|
||||
<div className="text-foreground-secondary">
|
||||
<span className="text-primary">User:</span> "Search the web for AI news and
|
||||
summarize it"
|
||||
</div>
|
||||
<div className="mt-3 text-foreground-secondary">
|
||||
<span className="text-primary">Agent:</span>
|
||||
</div>
|
||||
<div className="ml-4 mt-1 space-y-1 text-foreground-tertiary">
|
||||
<div>
|
||||
1. Calls <code className="text-primary">registrySearch</code>
|
||||
{`({ query: "web search" })`}
|
||||
</div>
|
||||
<div>
|
||||
2. Finds <code className="text-foreground">@exalabs/ai-sdk::webSearch</code>
|
||||
</div>
|
||||
<div>
|
||||
3. Calls <code className="text-primary">registryExecute</code>
|
||||
{`({ toolId: "@exalabs/ai-sdk::webSearch", params: {...} })`}
|
||||
</div>
|
||||
<div>4. Returns results to user</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How It Works */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">How It Works</h2>
|
||||
<div className="p-8 border border-border rounded-lg bg-surface font-mono text-sm overflow-x-auto">
|
||||
<pre className="text-foreground-secondary whitespace-pre">
|
||||
{`┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Your AI Agent │
|
||||
│ ┌─────────────┐ ┌────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Your Tools │ │ registrySearch │ │ registryExecute │ │
|
||||
│ └─────────────┘ └───────┬────────┘ └──────────┬──────────┘ │
|
||||
└───────────────────────────┼──────────────────────┼──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────────────┐
|
||||
│ TPMJS Registry │ │ Sandbox Executor │
|
||||
│ tpmjs.com/api │ │ executor.tpmjs.com │
|
||||
└─────────────────┘ └─────────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────────────┐
|
||||
│ Tool Metadata │ │ Secure Deno Runtime │
|
||||
│ 1000+ tools │ │ Isolated execution │
|
||||
└─────────────────┘ └─────────────────────────┘`}
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* registrySearchTool */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">registrySearchTool</h2>
|
||||
<p className="text-lg text-foreground-secondary mb-6">
|
||||
Search the TPMJS registry to find tools for any task. Returns metadata including the{' '}
|
||||
<code className="text-primary">toolId</code> needed for execution.
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Parameters */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Parameters</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left py-2 pr-4 text-foreground">Name</th>
|
||||
<th className="text-left py-2 pr-4 text-foreground">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-foreground">Required</th>
|
||||
<th className="text-left py-2 text-foreground">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-foreground-secondary">
|
||||
<tr className="border-b border-border/50">
|
||||
<td className="py-2 pr-4 font-mono text-primary">query</td>
|
||||
<td className="py-2 pr-4">string</td>
|
||||
<td className="py-2 pr-4">Yes</td>
|
||||
<td className="py-2">Search query (keywords, tool names, descriptions)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-border/50">
|
||||
<td className="py-2 pr-4 font-mono text-primary">category</td>
|
||||
<td className="py-2 pr-4">string</td>
|
||||
<td className="py-2 pr-4">No</td>
|
||||
<td className="py-2">Filter by category</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-primary">limit</td>
|
||||
<td className="py-2 pr-4">number</td>
|
||||
<td className="py-2 pr-4">No</td>
|
||||
<td className="py-2">Max results (1-20, default 5)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Categories */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Categories</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{[
|
||||
'web-scraping',
|
||||
'data-processing',
|
||||
'file-operations',
|
||||
'communication',
|
||||
'database',
|
||||
'api-integration',
|
||||
'image-processing',
|
||||
'text-analysis',
|
||||
'automation',
|
||||
'ai-ml',
|
||||
'security',
|
||||
'monitoring',
|
||||
].map((category) => (
|
||||
<span
|
||||
key={category}
|
||||
className="px-3 py-1 text-sm bg-background border border-border rounded-full text-foreground-secondary"
|
||||
>
|
||||
{category}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Return Value */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Return Value</h3>
|
||||
<CodeBlock
|
||||
language="json"
|
||||
code={`{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* registryExecuteTool */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">registryExecuteTool</h2>
|
||||
<p className="text-lg text-foreground-secondary mb-6">
|
||||
Execute any tool from the registry by its <code className="text-primary">toolId</code>
|
||||
. Tools run in a secure sandbox—no local installation required.
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Parameters */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Parameters</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left py-2 pr-4 text-foreground">Name</th>
|
||||
<th className="text-left py-2 pr-4 text-foreground">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-foreground">Required</th>
|
||||
<th className="text-left py-2 text-foreground">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-foreground-secondary">
|
||||
<tr className="border-b border-border/50">
|
||||
<td className="py-2 pr-4 font-mono text-primary">toolId</td>
|
||||
<td className="py-2 pr-4">string</td>
|
||||
<td className="py-2 pr-4">Yes</td>
|
||||
<td className="py-2">
|
||||
Tool identifier (format: <code>package::exportName</code>)
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="border-b border-border/50">
|
||||
<td className="py-2 pr-4 font-mono text-primary">params</td>
|
||||
<td className="py-2 pr-4">object</td>
|
||||
<td className="py-2 pr-4">Yes</td>
|
||||
<td className="py-2">Parameters to pass to the tool</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-primary">env</td>
|
||||
<td className="py-2 pr-4">object</td>
|
||||
<td className="py-2 pr-4">No</td>
|
||||
<td className="py-2">Environment variables (API keys)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Example */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Example</h3>
|
||||
<CodeBlock
|
||||
language="typescript"
|
||||
code={`// 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: [...] }
|
||||
// }`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Return Value */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<h3 className="text-xl font-semibold mb-4 text-foreground">Return Value</h3>
|
||||
<CodeBlock
|
||||
language="json"
|
||||
code={`{
|
||||
"toolId": "@exalabs/ai-sdk::webSearch",
|
||||
"executionTimeMs": 1234,
|
||||
"output": { ... }
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Environment Variables */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">Environment Variables</h2>
|
||||
<p className="text-lg text-foreground-secondary mb-6">
|
||||
Both packages support self-hosted registries via environment variables. This is useful
|
||||
for enterprise deployments or running your own tool registry.
|
||||
</p>
|
||||
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left py-2 pr-4 text-foreground">Variable</th>
|
||||
<th className="text-left py-2 pr-4 text-foreground">Default</th>
|
||||
<th className="text-left py-2 text-foreground">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-foreground-secondary">
|
||||
<tr className="border-b border-border/50">
|
||||
<td className="py-2 pr-4 font-mono text-primary">TPMJS_API_URL</td>
|
||||
<td className="py-2 pr-4 font-mono">https://tpmjs.com</td>
|
||||
<td className="py-2">Base URL for the registry API</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-primary">TPMJS_EXECUTOR_URL</td>
|
||||
<td className="py-2 pr-4 font-mono">https://executor.tpmjs.com</td>
|
||||
<td className="py-2">URL for the sandbox executor</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<h4 className="font-semibold mb-2 text-foreground">Self-Hosted Example</h4>
|
||||
<CodeBlock
|
||||
language="bash"
|
||||
code={`# Use your own TPMJS registry
|
||||
export TPMJS_API_URL=https://registry.mycompany.com
|
||||
export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Security */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">Security</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="text-2xl mb-2">🏝️</div>
|
||||
<h3 className="font-semibold mb-2 text-foreground">Sandboxed Execution</h3>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
All tools run in an isolated Deno runtime on Railway. They cannot access your
|
||||
local filesystem or environment.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="text-2xl mb-2">🔐</div>
|
||||
<h3 className="font-semibold mb-2 text-foreground">API Key Isolation</h3>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
API keys are passed per-request and never stored. Each execution is stateless and
|
||||
isolated.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="text-2xl mb-2">✅</div>
|
||||
<h3 className="font-semibold mb-2 text-foreground">Registry-Only Execution</h3>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Only tools registered in TPMJS can be executed. No arbitrary code execution is
|
||||
possible.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="text-2xl mb-2">🏥</div>
|
||||
<h3 className="font-semibold mb-2 text-foreground">Health Monitoring</h3>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Every tool is continuously health-checked. Broken tools are flagged and filtered
|
||||
from search results.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Vision & Future */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-6 text-foreground">The Vision</h2>
|
||||
<div className="prose max-w-none text-foreground-secondary text-lg space-y-4 mb-8">
|
||||
<p>
|
||||
We're building the{' '}
|
||||
<span className="text-foreground font-semibold">npm for AI tools</span>. Just as npm
|
||||
revolutionized JavaScript package sharing, TPMJS aims to create a universal
|
||||
ecosystem where AI agents can discover, share, and execute tools seamlessly.
|
||||
</p>
|
||||
<p>
|
||||
The <code className="text-primary">registrySearch</code> and{' '}
|
||||
<code className="text-primary">registryExecute</code> tools are just the beginning.
|
||||
Here's what's coming:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Collections */}
|
||||
<div className="p-6 border-2 border-primary/20 rounded-lg bg-primary/5">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="px-3 py-1 text-sm font-semibold bg-primary/20 text-primary rounded-full">
|
||||
Coming Soon
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">Collections</h3>
|
||||
</div>
|
||||
<p className="text-foreground-secondary mb-4">
|
||||
Pre-configured tool bundles for specific domains. Think of them as “skill
|
||||
packs” for your AI agent.
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="typescript"
|
||||
code={`// 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');`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* API Keys */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="px-3 py-1 text-sm font-semibold bg-foreground-tertiary/20 text-foreground-secondary rounded-full">
|
||||
Planned
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">
|
||||
API Keys & Rate Limiting
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-foreground-secondary">
|
||||
Personal API keys for authentication, usage tracking, and rate limiting.
|
||||
Enterprise features for teams including usage analytics and billing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Tool Versioning */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="px-3 py-1 text-sm font-semibold bg-foreground-tertiary/20 text-foreground-secondary rounded-full">
|
||||
Planned
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">Tool Versioning</h3>
|
||||
</div>
|
||||
<p className="text-foreground-secondary">
|
||||
Pin specific tool versions in your agent configuration. Automatic compatibility
|
||||
checking and migration guides when tools update.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Private Registries */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="px-3 py-1 text-sm font-semibold bg-foreground-tertiary/20 text-foreground-secondary rounded-full">
|
||||
Planned
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">Private Registries</h3>
|
||||
</div>
|
||||
<p className="text-foreground-secondary">
|
||||
Run your own TPMJS instance for internal tools. Connect multiple registries
|
||||
(public + private) in a single agent. Enterprise SSO and access controls.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Streaming */}
|
||||
<div className="p-6 border border-border rounded-lg bg-surface">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="px-3 py-1 text-sm font-semibold bg-foreground-tertiary/20 text-foreground-secondary rounded-full">
|
||||
Planned
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-foreground">Streaming Execution</h3>
|
||||
</div>
|
||||
<p className="text-foreground-secondary">
|
||||
Stream tool outputs for long-running operations. Real-time progress updates and
|
||||
partial results for better UX.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="text-center py-12 border border-border rounded-lg bg-surface">
|
||||
<h2 className="text-3xl font-bold mb-4 text-foreground">Ready to Get Started?</h2>
|
||||
<p className="text-lg text-foreground-secondary mb-8 max-w-2xl mx-auto">
|
||||
Give your AI agent access to thousands of tools in minutes.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||
<a
|
||||
href="https://www.npmjs.com/package/@tpmjs/registrySearch"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button size="lg" variant="default">
|
||||
View on npm
|
||||
</Button>
|
||||
</a>
|
||||
<Link href="/playground">
|
||||
<Button size="lg" variant="outline">
|
||||
Try in Playground
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/tool/tool-search">
|
||||
<Button size="lg" variant="outline">
|
||||
Browse Tools
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -43,6 +43,11 @@ export function AppHeader(): React.ReactElement {
|
|||
Spec
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/sdk">
|
||||
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
|
||||
SDK
|
||||
</Button>
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/tpmjs/tpmjs"
|
||||
target="_blank"
|
||||
|
|
|
|||
438
docs/REGISTRY_SDK_DESIGN.md
Normal file
438
docs/REGISTRY_SDK_DESIGN.md
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
# 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:
|
||||
|
||||
```typescript
|
||||
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`
|
||||
|
||||
```typescript
|
||||
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`
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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-tool", "ai-sdk", "vercel-ai", "tools", "registry", "search"],
|
||||
"peerDependencies": {
|
||||
"ai": "^4.0.0",
|
||||
"zod": "^3.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### package.json (@tpmjs/registryExecute)
|
||||
|
||||
```json
|
||||
{
|
||||
"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-tool", "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
|
||||
102
packages/tools/registryExecute/README.md
Normal file
102
packages/tools/registryExecute/README.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# @tpmjs/registryExecute
|
||||
|
||||
Execute tools from the TPMJS registry in any AI SDK agent. Tools run in a secure sandbox - no local installation required.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @tpmjs/registryExecute
|
||||
# or
|
||||
pnpm add @tpmjs/registryExecute
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
// The agent can now:
|
||||
// 1. Search for tools: registrySearch({ query: "web scraping" })
|
||||
// 2. Execute found tools: registryExecute({ toolId: "@firecrawl/ai-sdk::scrapeTool", params: { url: "..." } })
|
||||
```
|
||||
|
||||
## Tool: registryExecuteTool
|
||||
|
||||
Execute a tool from the TPMJS registry by its toolId.
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `toolId` | string | Yes | Tool identifier (format: `package::exportName`) |
|
||||
| `params` | object | Yes | Parameters to pass to the tool |
|
||||
| `env` | object | No | Environment variables (API keys) if required |
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
// 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: [...] }
|
||||
// }
|
||||
```
|
||||
|
||||
### Returns
|
||||
|
||||
```json
|
||||
{
|
||||
"toolId": "@exalabs/ai-sdk::webSearch",
|
||||
"executionTimeMs": 1234,
|
||||
"output": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## 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 |
|
||||
|
||||
### Self-Hosted Registry
|
||||
|
||||
To use your own TPMJS registry and executor:
|
||||
|
||||
```bash
|
||||
export TPMJS_API_URL=https://registry.mycompany.com
|
||||
export TPMJS_EXECUTOR_URL=https://executor.mycompany.com
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
- All tools run in a sandboxed Deno environment on Railway
|
||||
- API keys are passed per-request, never stored
|
||||
- Only registered tools can be executed (no arbitrary code)
|
||||
|
||||
## Related
|
||||
|
||||
- [@tpmjs/registrySearch](https://www.npmjs.com/package/@tpmjs/registrySearch) - Find tools to execute
|
||||
- [TPMJS Registry](https://tpmjs.com) - Browse all available tools
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
72
packages/tools/registryExecute/package.json
Normal file
72
packages/tools/registryExecute/package.json
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"name": "@tpmjs/registryExecute",
|
||||
"version": "0.1.0",
|
||||
"description": "Execute tools from the TPMJS registry in any AI SDK agent",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"keywords": ["tpmjs-tool", "ai-sdk", "vercel-ai", "registry", "execute"],
|
||||
"dependencies": {
|
||||
"ai": "6.0.0-beta.124"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tpmjs/tsconfig": "workspace:*",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"files": ["dist", "README.md"],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ajaxdavis/tpmjs.git",
|
||||
"directory": "packages/tools/registryExecute"
|
||||
},
|
||||
"homepage": "https://tpmjs.com",
|
||||
"license": "MIT",
|
||||
"tpmjs": {
|
||||
"category": "api-integration",
|
||||
"frameworks": ["vercel-ai"],
|
||||
"tools": [
|
||||
{
|
||||
"exportName": "registryExecuteTool",
|
||||
"description": "Execute a tool from the TPMJS registry by toolId. Use registrySearchTool first to find toolIds.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "toolId",
|
||||
"type": "string",
|
||||
"description": "Tool identifier from registrySearchTool (format: 'package::exportName')",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"type": "object",
|
||||
"description": "Parameters to pass to the tool",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "env",
|
||||
"type": "object",
|
||||
"description": "Environment variables (API keys) if required by the tool",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"type": "object",
|
||||
"description": "Tool execution result with output and timing"
|
||||
},
|
||||
"aiAgent": {
|
||||
"useCase": "Use after finding a tool with registrySearchTool. Execute the tool by its toolId with the required parameters.",
|
||||
"examples": [
|
||||
"Execute '@exalabs/ai-sdk::webSearch' with query parameter",
|
||||
"Execute '@firecrawl/ai-sdk::scrapeTool' with url parameter"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
120
packages/tools/registryExecute/src/index.ts
Normal file
120
packages/tools/registryExecute/src/index.ts
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import { jsonSchema, tool } from 'ai';
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* Input type for Registry Execute Tool
|
||||
*/
|
||||
type RegistryExecuteInput = {
|
||||
toolId: string;
|
||||
params: Record<string, unknown>;
|
||||
env?: Record<string, string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* AI SDK tool for executing tools from the TPMJS registry
|
||||
*
|
||||
* Use registrySearchTool first to find the toolId, then execute with this tool.
|
||||
* Tools run in a secure sandbox - no local installation required.
|
||||
*
|
||||
* Supports self-hosted registries via environment variables:
|
||||
* - TPMJS_API_URL: Registry API (default: https://tpmjs.com)
|
||||
* - TPMJS_EXECUTOR_URL: Sandbox executor (default: https://executor.tpmjs.com)
|
||||
*/
|
||||
export const registryExecuteTool = tool({
|
||||
description:
|
||||
'Execute a tool from the TPMJS registry. Use registrySearchTool first to find the toolId. Tools run in a secure sandbox.',
|
||||
inputSchema: jsonSchema<RegistryExecuteInput>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
toolId: {
|
||||
type: 'string',
|
||||
description: "Tool identifier from registrySearchTool (format: 'package::exportName')",
|
||||
},
|
||||
params: {
|
||||
type: 'object',
|
||||
description: 'Parameters to pass to the tool',
|
||||
additionalProperties: true,
|
||||
},
|
||||
env: {
|
||||
type: 'object',
|
||||
description: 'Environment variables (API keys) if required by the tool',
|
||||
additionalProperties: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['toolId', 'params'],
|
||||
additionalProperties: false,
|
||||
}),
|
||||
async execute({ toolId, params, env }) {
|
||||
// Parse toolId format: "package::exportName"
|
||||
const separatorIndex = toolId.lastIndexOf('::');
|
||||
if (separatorIndex === -1) {
|
||||
throw new Error(`Invalid toolId format. Expected "package::exportName", got "${toolId}"`);
|
||||
}
|
||||
|
||||
const packageName = toolId.substring(0, separatorIndex);
|
||||
const exportName = toolId.substring(separatorIndex + 2);
|
||||
|
||||
if (!packageName || !exportName) {
|
||||
throw new Error(`Invalid toolId format. Expected "package::exportName", got "${toolId}"`);
|
||||
}
|
||||
|
||||
// Fetch tool metadata to get version and importUrl
|
||||
const metaParams = new URLSearchParams({
|
||||
q: exportName,
|
||||
limit: '10',
|
||||
});
|
||||
const metaResponse = await fetch(`${TPMJS_API_URL}/api/tools/search?${metaParams}`);
|
||||
|
||||
if (!metaResponse.ok) {
|
||||
throw new Error(`Failed to fetch tool metadata: ${metaResponse.statusText}`);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: API response types vary
|
||||
const metaData = (await metaResponse.json()) as any;
|
||||
const toolsArray = metaData.results?.tools || [];
|
||||
|
||||
// Find the exact tool match
|
||||
// biome-ignore lint/suspicious/noExplicitAny: API response types vary
|
||||
const toolMeta = toolsArray.find(
|
||||
(t: any) => t.package.npmPackageName === packageName && t.exportName === exportName
|
||||
);
|
||||
|
||||
if (!toolMeta) {
|
||||
throw new Error(`Tool not found: ${toolId}`);
|
||||
}
|
||||
|
||||
const version = toolMeta.package.npmVersion;
|
||||
const importUrl = `https://esm.sh/${packageName}@${version}`;
|
||||
|
||||
// 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,
|
||||
importUrl,
|
||||
params,
|
||||
env: env || {},
|
||||
}),
|
||||
});
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: API response types vary
|
||||
const result = (await response.json()) as any;
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Tool execution failed');
|
||||
}
|
||||
|
||||
return {
|
||||
toolId,
|
||||
executionTimeMs: result.executionTimeMs,
|
||||
output: result.output,
|
||||
};
|
||||
},
|
||||
});
|
||||
12
packages/tools/registryExecute/tsconfig.json
Normal file
12
packages/tools/registryExecute/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "@tpmjs/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
102
packages/tools/registrySearch/README.md
Normal file
102
packages/tools/registrySearch/README.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# @tpmjs/registrySearch
|
||||
|
||||
Search the TPMJS tool registry from any AI SDK agent. Discover thousands of tools dynamically.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @tpmjs/registrySearch
|
||||
# or
|
||||
pnpm add @tpmjs/registrySearch
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
// The agent can now:
|
||||
// 1. Search for tools: registrySearch({ query: "web scraping" })
|
||||
// 2. Execute found tools: registryExecute({ toolId: "@firecrawl/ai-sdk::scrapeTool", params: { url: "..." } })
|
||||
```
|
||||
|
||||
## Tool: registrySearchTool
|
||||
|
||||
Search the TPMJS registry to find AI SDK tools.
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `query` | string | Yes | Search query (keywords, tool names, descriptions) |
|
||||
| `category` | string | No | Filter by category |
|
||||
| `limit` | number | No | Max results (1-20, default 5) |
|
||||
|
||||
### Categories
|
||||
|
||||
- `web-scraping`
|
||||
- `data-processing`
|
||||
- `file-operations`
|
||||
- `communication`
|
||||
- `database`
|
||||
- `api-integration`
|
||||
- `image-processing`
|
||||
- `text-analysis`
|
||||
- `automation`
|
||||
- `ai-ml`
|
||||
- `security`
|
||||
- `monitoring`
|
||||
|
||||
### Returns
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `TPMJS_API_URL` | `https://tpmjs.com` | Base URL for the registry API |
|
||||
|
||||
### Self-Hosted Registry
|
||||
|
||||
To use your own TPMJS registry:
|
||||
|
||||
```bash
|
||||
export TPMJS_API_URL=https://registry.mycompany.com
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [@tpmjs/registryExecute](https://www.npmjs.com/package/@tpmjs/registryExecute) - Execute tools found with this package
|
||||
- [TPMJS Registry](https://tpmjs.com) - Browse all available tools
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
73
packages/tools/registrySearch/package.json
Normal file
73
packages/tools/registrySearch/package.json
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"name": "@tpmjs/registrySearch",
|
||||
"version": "0.1.0",
|
||||
"description": "Search the TPMJS tool registry from any AI SDK agent",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"keywords": ["tpmjs-tool", "ai-sdk", "vercel-ai", "registry", "search"],
|
||||
"dependencies": {
|
||||
"ai": "6.0.0-beta.124"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tpmjs/tsconfig": "workspace:*",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"files": ["dist", "README.md"],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ajaxdavis/tpmjs.git",
|
||||
"directory": "packages/tools/registrySearch"
|
||||
},
|
||||
"homepage": "https://tpmjs.com",
|
||||
"license": "MIT",
|
||||
"tpmjs": {
|
||||
"category": "api-integration",
|
||||
"frameworks": ["vercel-ai"],
|
||||
"tools": [
|
||||
{
|
||||
"exportName": "registrySearchTool",
|
||||
"description": "Search the TPMJS tool registry to find AI SDK tools. Returns tool metadata including toolId for use with registryExecuteTool.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "query",
|
||||
"type": "string",
|
||||
"description": "Search query (keywords, tool names, descriptions)",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"type": "string",
|
||||
"description": "Filter by category (optional)",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"type": "number",
|
||||
"description": "Maximum results (1-20, default 5)",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"type": "object",
|
||||
"description": "Search results with tool metadata including toolId, packageName, exportName, description, requiredEnvVars"
|
||||
},
|
||||
"aiAgent": {
|
||||
"useCase": "Use when you need to find a tool for a specific task. Search returns toolIds that can be executed with registryExecuteTool.",
|
||||
"examples": [
|
||||
"Search for 'web scraping' to find scraping tools",
|
||||
"Search for 'weather' to find weather API tools",
|
||||
"Search for 'database' to find database tools"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
102
packages/tools/registrySearch/src/index.ts
Normal file
102
packages/tools/registrySearch/src/index.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import { jsonSchema, tool } from 'ai';
|
||||
|
||||
const TPMJS_API_URL = process.env.TPMJS_API_URL || 'https://tpmjs.com';
|
||||
|
||||
/**
|
||||
* Input type for Registry Search Tool
|
||||
*/
|
||||
type RegistrySearchInput = {
|
||||
query: string;
|
||||
category?: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* AI SDK tool for searching the TPMJS tool registry
|
||||
*
|
||||
* This tool enables agents to discover tools dynamically from the TPMJS registry.
|
||||
* Search results include toolIds that can be executed with @tpmjs/registryExecute.
|
||||
*
|
||||
* Supports self-hosted registries via TPMJS_API_URL environment variable.
|
||||
*/
|
||||
export const registrySearchTool = tool({
|
||||
description:
|
||||
'Search the TPMJS tool registry to find AI SDK tools. Use this to discover tools for any task. Returns toolIds that can be executed with registryExecuteTool.',
|
||||
inputSchema: jsonSchema<RegistrySearchInput>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'Search query (keywords, tool names, descriptions)',
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
description: 'Filter by tool category (optional)',
|
||||
enum: [
|
||||
'web-scraping',
|
||||
'data-processing',
|
||||
'file-operations',
|
||||
'communication',
|
||||
'database',
|
||||
'api-integration',
|
||||
'image-processing',
|
||||
'text-analysis',
|
||||
'automation',
|
||||
'ai-ml',
|
||||
'security',
|
||||
'monitoring',
|
||||
],
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Maximum number of results (1-20, default 5)',
|
||||
minimum: 1,
|
||||
maximum: 20,
|
||||
},
|
||||
},
|
||||
required: ['query'],
|
||||
additionalProperties: false,
|
||||
}),
|
||||
async execute({ query, category, limit = 5 }) {
|
||||
const params = new URLSearchParams({
|
||||
q: query,
|
||||
limit: String(limit),
|
||||
...(category && { category }),
|
||||
});
|
||||
|
||||
const url = `${TPMJS_API_URL}/api/tools/search?${params}`;
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Search failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: API response types vary
|
||||
const data = (await response.json()) as any;
|
||||
const toolsArray = data.results?.tools || [];
|
||||
|
||||
return {
|
||||
query,
|
||||
matchCount: toolsArray.length,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Tool types from API vary
|
||||
tools: toolsArray.map((t: any) => ({
|
||||
// Unique identifier for registryExecuteTool
|
||||
toolId: `${t.package.npmPackageName}::${t.exportName}`,
|
||||
|
||||
// Human-readable info
|
||||
name: t.exportName,
|
||||
package: t.package.npmPackageName,
|
||||
description: t.description,
|
||||
category: t.package.category,
|
||||
|
||||
// Execution requirements
|
||||
requiredEnvVars:
|
||||
t.package.env?.filter((e: any) => e.required).map((e: any) => e.name) || [],
|
||||
|
||||
// Quality indicators
|
||||
healthStatus: t.executionHealth,
|
||||
qualityScore: t.qualityScore,
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
12
packages/tools/registrySearch/tsconfig.json
Normal file
12
packages/tools/registrySearch/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "@tpmjs/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
|
|
@ -131,10 +131,10 @@ importers:
|
|||
version: 10.4.22(postcss@8.5.6)
|
||||
eslint:
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1(jiti@2.6.1)
|
||||
version: 9.39.1(jiti@1.21.7)
|
||||
eslint-config-next:
|
||||
specifier: ^16.0.4
|
||||
version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
|
||||
postcss:
|
||||
specifier: ^8.5.1
|
||||
version: 8.5.6
|
||||
|
|
@ -249,10 +249,10 @@ importers:
|
|||
version: 10.4.22(postcss@8.5.6)
|
||||
eslint:
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1(jiti@1.21.7)
|
||||
version: 9.39.1(jiti@2.6.1)
|
||||
eslint-config-next:
|
||||
specifier: ^16.0.4
|
||||
version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)
|
||||
version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
postcss:
|
||||
specifier: ^8.5.1
|
||||
version: 8.5.6
|
||||
|
|
@ -600,6 +600,32 @@ importers:
|
|||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
|
||||
packages/tools/registryExecute:
|
||||
dependencies:
|
||||
ai:
|
||||
specifier: 6.0.0-beta.124
|
||||
version: 6.0.0-beta.124(effect@3.18.4)(zod@4.1.13)
|
||||
devDependencies:
|
||||
'@tpmjs/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/tsconfig
|
||||
typescript:
|
||||
specifier: ^5.7.2
|
||||
version: 5.9.3
|
||||
|
||||
packages/tools/registrySearch:
|
||||
dependencies:
|
||||
ai:
|
||||
specifier: 6.0.0-beta.124
|
||||
version: 6.0.0-beta.124(effect@3.18.4)(zod@4.1.13)
|
||||
devDependencies:
|
||||
'@tpmjs/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/tsconfig
|
||||
typescript:
|
||||
specifier: ^5.7.2
|
||||
version: 5.9.3
|
||||
|
||||
packages/tools/search-registry:
|
||||
dependencies:
|
||||
ai:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue