content: update tutorial slides with grounded TPMJS content
- WelcomeSlide: "The missing layer between npm and AI agents" - ProblemSlide: "npm has 2 million packages. Which ones work?" - SolutionSlide: Registry that extracts schemas, scores quality, checks health - HowItWorksSlide: Automated pipeline flow - DiscoverySlide: Schema extraction from sandbox - IntegrationSlide: Quality scoring algorithm - QualitySlide: Health checks (import + execution) - ToolDetailSlide: What we store (inputSchema, returnSchema, envKeys, tier) - GetStartedSlide: CTAs with accurate descriptions All content now grounded in actual codebase functionality. Includes TPMJS_TALK.md as source of truth document.
This commit is contained in:
parent
b279be2a25
commit
70d0c5ba94
10 changed files with 532 additions and 121 deletions
391
TPMJS_TALK.md
Normal file
391
TPMJS_TALK.md
Normal file
|
|
@ -0,0 +1,391 @@
|
||||||
|
# TPMJS: The Missing Layer Between "LLMs Can Call Tools" and "Which Tool, Exactly?"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Setup
|
||||||
|
|
||||||
|
You're building an AI agent. It needs to do things in the world—scrape a webpage, send an email, query a database, generate an image. These capabilities come from **tools**.
|
||||||
|
|
||||||
|
The problem isn't that tools don't exist. They do. Thousands of them. The problem is:
|
||||||
|
|
||||||
|
- **You can't find them.** npm has 2 million packages. Which ones are AI-callable tools? Which ones actually work?
|
||||||
|
- **You can't trust them.** No schema. No examples. README says "AI-ready" but the function signature is `(opts: any) => Promise<any>`.
|
||||||
|
- **You can't compare them.** Three packages do "web scraping." Which one handles JavaScript rendering? Which one returns structured data? Which one is maintained?
|
||||||
|
|
||||||
|
Discovery is the bottleneck. Not capability—discovery.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What TPMJS Actually Is
|
||||||
|
|
||||||
|
TPMJS is infrastructure. Specifically:
|
||||||
|
|
||||||
|
1. **A registry** that indexes npm packages designed for AI tool use
|
||||||
|
2. **A metadata extraction pipeline** that pulls schemas directly from code
|
||||||
|
3. **A quality scoring system** that ranks tools by completeness and adoption
|
||||||
|
4. **A health monitoring system** that verifies tools actually work
|
||||||
|
5. **A playground** where you can test tools before integrating them
|
||||||
|
|
||||||
|
It's not magic. It's plumbing. Good plumbing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How It Works (The Technical Reality)
|
||||||
|
|
||||||
|
### Discovery: Finding Tools in the Wild
|
||||||
|
|
||||||
|
TPMJS runs three automated sync jobs:
|
||||||
|
|
||||||
|
**1. npm Changes Feed (every 2 minutes)**
|
||||||
|
```
|
||||||
|
npm registry → /_changes endpoint → filter for tpmjs-tool keyword → process
|
||||||
|
```
|
||||||
|
This catches new packages and updates in near-real-time. We track sequence numbers so we never reprocess.
|
||||||
|
|
||||||
|
**2. Keyword Search (every 15 minutes)**
|
||||||
|
```
|
||||||
|
npm search "tpmjs-tool" → up to 250 results → validate → ingest
|
||||||
|
```
|
||||||
|
Backup mechanism. Catches anything the changes feed missed.
|
||||||
|
|
||||||
|
**3. Metrics Sync (hourly)**
|
||||||
|
```
|
||||||
|
for each package → fetch download stats → recalculate quality scores → update health status
|
||||||
|
```
|
||||||
|
Keeps the registry fresh.
|
||||||
|
|
||||||
|
### The Publisher Contract
|
||||||
|
|
||||||
|
To get indexed, a package needs two things:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@acme/my-tool",
|
||||||
|
"keywords": ["tpmjs-tool"],
|
||||||
|
"tpmjs": {
|
||||||
|
"category": "web-scraping",
|
||||||
|
"description": "Scrapes URLs and returns structured markdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That's the minimum. Category + description. Everything else is either optional or auto-extracted.
|
||||||
|
|
||||||
|
**Categories are fixed** (12 total): web-scraping, data-processing, file-operations, communication, database, api-integration, image-processing, text-analysis, automation, ai-ml, security, monitoring.
|
||||||
|
|
||||||
|
Why fixed? Because agents need to filter. "Give me all database tools" has to mean something.
|
||||||
|
|
||||||
|
### Schema Extraction: The Hard Part
|
||||||
|
|
||||||
|
Here's what makes TPMJS different from a glorified npm search.
|
||||||
|
|
||||||
|
When we ingest a package, we don't just read the README. We **execute it in a sandbox** and extract the actual schema:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Spin up isolated executor (Railway)
|
||||||
|
2. npm install the package
|
||||||
|
3. Import and inspect exports
|
||||||
|
4. Extract JSON Schema from TypeScript types
|
||||||
|
5. Store schema in database
|
||||||
|
```
|
||||||
|
|
||||||
|
The result:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "scrapeUrl",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": { "type": "string", "format": "uri" },
|
||||||
|
"waitForSelector": { "type": "string" },
|
||||||
|
"timeout": { "type": "number", "default": 30000 }
|
||||||
|
},
|
||||||
|
"required": ["url"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This isn't documentation. This is **extracted from the actual function signature**. It's ground truth.
|
||||||
|
|
||||||
|
If the author provides a schema in the `tpmjs` field, we use that. If not, we extract it. Either way, every tool in the registry has a schema.
|
||||||
|
|
||||||
|
### Quality Scoring: Ranking What Matters
|
||||||
|
|
||||||
|
Every tool gets a score from 0.00 to 1.00:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Base score from metadata completeness
|
||||||
|
const tierScore = tier === 'rich' ? 0.6 : 0.4;
|
||||||
|
|
||||||
|
// Adoption signals
|
||||||
|
const downloadsScore = Math.min(0.2, Math.log10(downloads + 1) / 15);
|
||||||
|
const starsScore = Math.min(0.1, Math.log10(githubStars + 1) / 10);
|
||||||
|
|
||||||
|
// Metadata richness bonus
|
||||||
|
let richnessScore = 0;
|
||||||
|
if (hasParameters) richnessScore += 0.04;
|
||||||
|
if (hasReturns) richnessScore += 0.03;
|
||||||
|
if (hasEnvVars) richnessScore += 0.03;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tier** is binary:
|
||||||
|
- **Minimal**: Just category + description (40% base)
|
||||||
|
- **Rich**: Has parameters, returns, env vars, or framework tags (60% base)
|
||||||
|
|
||||||
|
The formula is deliberately simple. We're not trying to be clever. We're trying to surface tools that are well-documented and actually used.
|
||||||
|
|
||||||
|
### Health Checks: Does It Actually Work?
|
||||||
|
|
||||||
|
Two checks, run during sync and periodically:
|
||||||
|
|
||||||
|
**1. Import Health**
|
||||||
|
```
|
||||||
|
Can we require() this package without it exploding?
|
||||||
|
```
|
||||||
|
You'd be surprised how many npm packages fail this.
|
||||||
|
|
||||||
|
**2. Execution Health**
|
||||||
|
```
|
||||||
|
Can we call the main function with minimal parameters without throwing?
|
||||||
|
```
|
||||||
|
Not a full test suite. Just "does it run at all?"
|
||||||
|
|
||||||
|
Results: `HEALTHY`, `BROKEN`, or `UNKNOWN`.
|
||||||
|
|
||||||
|
Broken tools still appear in the registry (with a warning). We don't hide them—we label them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Data Model
|
||||||
|
|
||||||
|
Here's what we actually store:
|
||||||
|
|
||||||
|
### Package (npm package level)
|
||||||
|
```
|
||||||
|
npmPackageName (unique)
|
||||||
|
npmVersion, npmDescription, npmRepository, npmLicense
|
||||||
|
npmKeywords[], npmReadme, npmAuthor
|
||||||
|
category (enum)
|
||||||
|
tier ('minimal' | 'rich')
|
||||||
|
discoveryMethod ('changes-feed' | 'keyword')
|
||||||
|
npmDownloadsLastMonth, githubStars
|
||||||
|
frameworks[] (vercel-ai, langchain, etc.)
|
||||||
|
env[] (required environment variables)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tool (individual callable within a package)
|
||||||
|
```
|
||||||
|
packageId (FK)
|
||||||
|
name (export name: "scrapeUrl", "default", etc.)
|
||||||
|
description
|
||||||
|
inputSchema (JSON Schema)
|
||||||
|
schemaSource ('extracted' | 'author')
|
||||||
|
qualityScore (0.00-1.00)
|
||||||
|
importHealth, executionHealth (HEALTHY | BROKEN | UNKNOWN)
|
||||||
|
toolDiscoverySource ('auto' | 'manual')
|
||||||
|
```
|
||||||
|
|
||||||
|
One package can have multiple tools. `@acme/web-tools` might export `scrapeUrl`, `screenshotPage`, and `extractLinks`. Each is a separate tool with its own schema and health status.
|
||||||
|
|
||||||
|
### Simulation (playground execution)
|
||||||
|
```
|
||||||
|
toolId
|
||||||
|
userPrompt (what the user asked)
|
||||||
|
parameters (JSON, what was passed to the tool)
|
||||||
|
status (pending | running | success | error | timeout)
|
||||||
|
executionTimeMs, output, error
|
||||||
|
model, agentSteps
|
||||||
|
```
|
||||||
|
|
||||||
|
We track every playground execution. Not for surveillance—for debugging and improving the system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The API
|
||||||
|
|
||||||
|
### Search & Discovery
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/tools
|
||||||
|
?q=scrape
|
||||||
|
&category=web-scraping
|
||||||
|
&importHealth=HEALTHY
|
||||||
|
&executionHealth=HEALTHY
|
||||||
|
&limit=20
|
||||||
|
&offset=0
|
||||||
|
|
||||||
|
→ Returns tools sorted by quality score
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/tools/search
|
||||||
|
?q=I need to extract text from PDFs
|
||||||
|
|
||||||
|
→ BM25-ranked semantic search
|
||||||
|
```
|
||||||
|
|
||||||
|
### Execution
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/tools/execute/{toolId}
|
||||||
|
{
|
||||||
|
"prompt": "Scrape the homepage of Hacker News",
|
||||||
|
"parameters": { "url": "https://news.ycombinator.com" }
|
||||||
|
}
|
||||||
|
|
||||||
|
→ Server-Sent Events stream with:
|
||||||
|
- Agent reasoning steps
|
||||||
|
- Tool call results
|
||||||
|
- Final output
|
||||||
|
```
|
||||||
|
|
||||||
|
Rate limited: 10 requests per IP per hour. We're not a free compute platform.
|
||||||
|
|
||||||
|
### Schema Operations
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/tools/extract-schema
|
||||||
|
{ "packageName": "@acme/my-tool", "toolName": "scrapeUrl" }
|
||||||
|
|
||||||
|
→ Forces re-extraction of schema from source
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Playground
|
||||||
|
|
||||||
|
A Next.js app where you can:
|
||||||
|
|
||||||
|
1. **Browse tools** by category, health status, quality score
|
||||||
|
2. **Inspect schemas** before you commit to anything
|
||||||
|
3. **Test execution** with an AI agent
|
||||||
|
4. **See real responses** with actual latency and token usage
|
||||||
|
|
||||||
|
It's not a demo. It's a debugging tool. "Does this tool do what I think it does?" Answer that question in 30 seconds instead of 30 minutes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What This Enables
|
||||||
|
|
||||||
|
### For Engineers Building Agents
|
||||||
|
|
||||||
|
Before TPMJS:
|
||||||
|
```
|
||||||
|
1. Search npm for "web scraper"
|
||||||
|
2. Get 500 results
|
||||||
|
3. Click through 20 of them
|
||||||
|
4. Read READMEs that say "easy to use!"
|
||||||
|
5. npm install three of them
|
||||||
|
6. Write test code for each
|
||||||
|
7. Find out two are broken
|
||||||
|
8. Pick the one that works
|
||||||
|
9. Hope it keeps working
|
||||||
|
```
|
||||||
|
|
||||||
|
After TPMJS:
|
||||||
|
```
|
||||||
|
1. Search tpmjs.com for "web scraper"
|
||||||
|
2. Filter by HEALTHY status
|
||||||
|
3. Sort by quality score
|
||||||
|
4. Click top result
|
||||||
|
5. See exact input schema
|
||||||
|
6. Test in playground
|
||||||
|
7. Integrate
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Tool Authors
|
||||||
|
|
||||||
|
Before TPMJS:
|
||||||
|
```
|
||||||
|
Publish to npm → hope someone finds it → no visibility into usage
|
||||||
|
```
|
||||||
|
|
||||||
|
After TPMJS:
|
||||||
|
```
|
||||||
|
Publish to npm with tpmjs-tool keyword → indexed within 2 minutes →
|
||||||
|
schema auto-extracted → quality scored → discoverable by search →
|
||||||
|
execution stats tracked
|
||||||
|
```
|
||||||
|
|
||||||
|
Your tool becomes findable. Not just by humans grepping npm, but by agents querying the registry API.
|
||||||
|
|
||||||
|
### For Agents (Yes, Really)
|
||||||
|
|
||||||
|
Agents can query TPMJS at runtime:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const tools = await fetch('https://tpmjs.com/api/tools?' + new URLSearchParams({
|
||||||
|
q: 'send email',
|
||||||
|
executionHealth: 'HEALTHY',
|
||||||
|
limit: '5'
|
||||||
|
})).then(r => r.json());
|
||||||
|
|
||||||
|
// Agent now has 5 working email tools with full schemas
|
||||||
|
// It can pick the best one for this specific task
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the endgame. Not humans browsing a registry—agents dynamically selecting tools based on capability, health, and fit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What TPMJS Is Not
|
||||||
|
|
||||||
|
**Not a package manager.** We don't host packages. npm does that. We index and enrich.
|
||||||
|
|
||||||
|
**Not an execution platform.** The playground runs tools for testing. Production execution is your responsibility.
|
||||||
|
|
||||||
|
**Not a security guarantee.** We check if tools work. We don't audit them for malice. Same rules as npm: don't run untrusted code.
|
||||||
|
|
||||||
|
**Not magic.** We're not using AI to understand what tools do. We're extracting schemas and running health checks. Boring, reliable, debuggable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Technical Stack
|
||||||
|
|
||||||
|
- **Database**: PostgreSQL via Prisma
|
||||||
|
- **Web**: Next.js 16 (App Router)
|
||||||
|
- **Deployment**: Vercel (web) + Railway (sandbox executor)
|
||||||
|
- **Sync**: Vercel Cron + GitHub Actions backup
|
||||||
|
- **AI**: Vercel AI SDK for playground execution
|
||||||
|
- **Monorepo**: Turborepo + pnpm
|
||||||
|
|
||||||
|
Key internal packages:
|
||||||
|
- `@tpmjs/npm-client` — npm registry integration
|
||||||
|
- `@tpmjs/package-executor` — sandbox execution client
|
||||||
|
- `@tpmjs/types` — schema validation and migration
|
||||||
|
- `@tpmjs/db` — Prisma client and models
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- **~100 tools indexed** (and growing with every npm publish)
|
||||||
|
- **12 categories** covering most agent use cases
|
||||||
|
- **Automated sync** running 24/7
|
||||||
|
- **Health checks** on every tool
|
||||||
|
- **Schema extraction** working for TypeScript and JavaScript
|
||||||
|
- **Playground** functional for testing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Pitch (Finally)
|
||||||
|
|
||||||
|
Tools are the API surface of AI agents. The ecosystem is a mess. TPMJS is the index.
|
||||||
|
|
||||||
|
We don't compete with npm—we sit on top of it. We don't replace tool authors—we make them discoverable. We don't build agents—we give agents a way to find their tools.
|
||||||
|
|
||||||
|
Discovery is the bottleneck. We're fixing discovery.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Try It
|
||||||
|
|
||||||
|
- **Browse**: https://tpmjs.com/tool-search
|
||||||
|
- **Playground**: https://tpmjs.com/playground
|
||||||
|
- **Publish**: Add `tpmjs-tool` keyword + `tpmjs` field to your package.json
|
||||||
|
- **API**: `GET https://tpmjs.com/api/tools`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Tools are inevitable. Discovery chaos isn't.*
|
||||||
|
|
@ -24,55 +24,54 @@ export function DiscoverySlide(): React.ReactElement {
|
||||||
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
|
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
|
||||||
className="text-6xl mb-8"
|
className="text-6xl mb-8"
|
||||||
>
|
>
|
||||||
🔍
|
⚙️
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Two Users, One Surface</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Schema Extraction</h2>
|
||||||
<p className="text-xl text-white/40 mb-12">
|
<p className="text-xl text-white/40 mb-12">
|
||||||
Same registry. <span className="text-cyan-400 font-semibold">Different clients.</span>
|
The hard part.{' '}
|
||||||
|
<span className="text-cyan-400 font-semibold">We run your code in a sandbox.</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Two user types */}
|
{/* Code block showing extraction */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-8 max-w-4xl mx-auto">
|
<motion.div
|
||||||
<motion.div
|
initial={{ opacity: 0, y: 20 }}
|
||||||
initial={{ opacity: 0, x: -30 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
transition={{ delay: 0.5 }}
|
||||||
transition={{ delay: 0.5 }}
|
className="max-w-2xl mx-auto p-6 rounded-xl bg-[#1e1e2e] text-left font-mono text-sm"
|
||||||
className="p-6 rounded-2xl bg-white/5 border border-white/10 text-left"
|
>
|
||||||
>
|
<div className="text-white/40 mb-3">
|
||||||
<div className="text-4xl mb-4">👨💻</div>
|
{/* comment */}
|
||||||
<div className="text-2xl font-bold text-cyan-400 mb-3">Engineers browsing</div>
|
{'// We extract this from your actual code:'}
|
||||||
<ul className="space-y-2 text-white/60">
|
</div>
|
||||||
<li>"Show me the best extraction tools"</li>
|
<div className="text-purple-400">inputSchema: {'{'}</div>
|
||||||
<li>"I need one that supports screenshots"</li>
|
<div className="text-white/80 pl-4">
|
||||||
<li>"I need maintained + documented"</li>
|
type: <span className="text-emerald-400">"object"</span>,
|
||||||
</ul>
|
</div>
|
||||||
</motion.div>
|
<div className="text-white/80 pl-4">properties: {'{'}</div>
|
||||||
|
<div className="text-white/80 pl-8">
|
||||||
<motion.div
|
url: {'{'} type: <span className="text-emerald-400">"string"</span>, format:{' '}
|
||||||
initial={{ opacity: 0, x: 30 }}
|
<span className="text-emerald-400">"uri"</span> {'}'}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
</div>
|
||||||
transition={{ delay: 0.7 }}
|
<div className="text-white/80 pl-8">
|
||||||
className="p-6 rounded-2xl bg-white/5 border border-white/10 text-left"
|
timeout: {'{'} type: <span className="text-emerald-400">"number"</span>,
|
||||||
>
|
default: <span className="text-cyan-400">30000</span> {'}'}
|
||||||
<div className="text-4xl mb-4">🤖</div>
|
</div>
|
||||||
<div className="text-2xl font-bold text-purple-400 mb-3">Agents selecting</div>
|
<div className="text-white/80 pl-4">{'}'}</div>
|
||||||
<ul className="space-y-2 text-white/60">
|
<div className="text-purple-400">{'}'}</div>
|
||||||
<li>"I need to scrape a URL into markdown"</li>
|
</motion.div>
|
||||||
<li>"I need to summarize a PDF with citations"</li>
|
|
||||||
<li>"I need a company research tool"</li>
|
|
||||||
</ul>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Key insight */}
|
{/* Key insight */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ delay: 1.2 }}
|
transition={{ delay: 1.2 }}
|
||||||
className="mt-12 text-white/40 text-lg"
|
className="mt-8 text-white/40 text-lg"
|
||||||
>
|
>
|
||||||
Results are <span className="text-emerald-400 font-semibold">tool-shaped</span>, not just package-shaped.
|
Not documentation.{' '}
|
||||||
|
<span className="text-emerald-400 font-semibold">
|
||||||
|
Ground truth from TypeScript types.
|
||||||
|
</span>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,21 @@ const links = [
|
||||||
{
|
{
|
||||||
label: 'Browse Tools',
|
label: 'Browse Tools',
|
||||||
href: 'https://tpmjs.com/tool-search',
|
href: 'https://tpmjs.com/tool-search',
|
||||||
description: 'Use as a discovery portal',
|
description: 'Search by name, category, quality',
|
||||||
icon: '🔍',
|
icon: '🔍',
|
||||||
gradient: 'from-cyan-500 to-blue-500',
|
gradient: 'from-cyan-500 to-blue-500',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Publish a Tool',
|
label: 'Publish a Tool',
|
||||||
href: 'https://tpmjs.com/docs/publishing',
|
href: 'https://tpmjs.com/docs/publishing',
|
||||||
description: 'Clean metadata + examples',
|
description: 'Add tpmjs-tool keyword to npm',
|
||||||
icon: '📦',
|
icon: '📦',
|
||||||
gradient: 'from-purple-500 to-pink-500',
|
gradient: 'from-purple-500 to-pink-500',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'The Playground',
|
label: 'The Playground',
|
||||||
href: 'https://tpmjs.com/playground',
|
href: 'https://tpmjs.com/playground',
|
||||||
description: 'Try tools before adopting',
|
description: 'Run tools in browser sandbox',
|
||||||
icon: '🎮',
|
icon: '🎮',
|
||||||
gradient: 'from-emerald-500 to-teal-500',
|
gradient: 'from-emerald-500 to-teal-500',
|
||||||
},
|
},
|
||||||
|
|
@ -61,8 +61,10 @@ export function GetStartedSlide(): React.ReactElement {
|
||||||
🚀
|
🚀
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2 className="text-5xl md:text-7xl font-bold text-white mb-4">Try It</h2>
|
<h2 className="text-5xl md:text-7xl font-bold text-white mb-4">Get Started</h2>
|
||||||
<p className="text-xl md:text-2xl text-white/40 mb-16">Discovery chaos is optional</p>
|
<p className="text-xl md:text-2xl text-white/40 mb-16">
|
||||||
|
Indexed automatically. Updated every 2 minutes.
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* CTA buttons */}
|
{/* CTA buttons */}
|
||||||
<div className="flex flex-col md:flex-row items-center justify-center gap-6">
|
<div className="flex flex-col md:flex-row items-center justify-center gap-6">
|
||||||
|
|
@ -100,7 +102,13 @@ export function GetStartedSlide(): React.ReactElement {
|
||||||
initial={{ x: -10 }}
|
initial={{ x: -10 }}
|
||||||
whileHover={{ x: 0 }}
|
whileHover={{ x: 0 }}
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="w-5 h-5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|
@ -120,7 +128,7 @@ export function GetStartedSlide(): React.ReactElement {
|
||||||
transition={{ delay: 1.2 }}
|
transition={{ delay: 1.2 }}
|
||||||
className="mt-16 text-white/30 text-sm"
|
className="mt-16 text-white/30 text-sm"
|
||||||
>
|
>
|
||||||
Tools are inevitable. Discovery chaos isn't.
|
The missing layer between npm and AI agents.
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{ icon: '📝', label: 'What it does', description: 'Description, category, tags' },
|
{ icon: '📦', label: 'npm publish', description: 'Add tpmjs-tool keyword' },
|
||||||
{ icon: '⚙️', label: 'How to call it', description: 'Inputs, schema, examples' },
|
{ icon: '🔄', label: 'Auto-sync', description: 'Indexed in ~2 minutes' },
|
||||||
{ icon: '🔑', label: 'What it needs', description: 'Env vars, auth hints' },
|
{ icon: '⚙️', label: 'Schema extracted', description: 'From actual code' },
|
||||||
{ icon: '📊', label: 'Signals', description: 'Downloads, health, freshness' },
|
{ icon: '✅', label: 'Health checked', description: 'Import + execution' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function HowItWorksSlide(): React.ReactElement {
|
export function HowItWorksSlide(): React.ReactElement {
|
||||||
|
|
@ -21,13 +21,13 @@ export function HowItWorksSlide(): React.ReactElement {
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
className="relative z-10 max-w-5xl w-full"
|
className="relative z-10 max-w-5xl w-full"
|
||||||
>
|
>
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">What TPMJS Stores</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">How It Works</h2>
|
||||||
<p className="text-xl text-white/40 mb-16">For each tool, we expose the useful stuff</p>
|
<p className="text-xl text-white/40 mb-16">Automated pipeline. No manual curation.</p>
|
||||||
|
|
||||||
{/* Flow diagram */}
|
{/* Flow diagram */}
|
||||||
<div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-0">
|
<div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-0">
|
||||||
{steps.map((step, index) => (
|
{steps.map((step, index) => (
|
||||||
<div key={index} className="flex items-center">
|
<div key={step.label} className="flex items-center">
|
||||||
{/* Step card */}
|
{/* Step card */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
|
@ -61,6 +61,7 @@ export function HowItWorksSlide(): React.ReactElement {
|
||||||
>
|
>
|
||||||
<div className="w-12 h-px bg-gradient-to-r from-cyan-500/50 to-purple-500/50" />
|
<div className="w-12 h-px bg-gradient-to-r from-cyan-500/50 to-purple-500/50" />
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
className="w-4 h-4 text-purple-500/50 -ml-1"
|
className="w-4 h-4 text-purple-500/50 -ml-1"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,13 @@ import { motion } from 'framer-motion';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
const codeLines = [
|
const codeLines = [
|
||||||
{ text: '// Faster tool evaluation', delay: 0.5 },
|
{ text: '// Quality score: 0.00 - 1.00', delay: 0.5 },
|
||||||
{ text: 'const tool = await tpmjs.find({', delay: 0.8 },
|
{ text: "const tier = tier === 'rich' ? 0.6 : 0.4;", delay: 0.8 },
|
||||||
{ text: " capability: 'web-scraping',", delay: 1.1 },
|
{ text: 'const downloads = Math.min(0.2, log10(d));', delay: 1.1 },
|
||||||
{ text: " minQuality: 0.8", delay: 1.4 },
|
{ text: 'const stars = Math.min(0.1, log10(s));', delay: 1.4 },
|
||||||
{ text: '});', delay: 1.7 },
|
{ text: '', delay: 1.7 },
|
||||||
{ text: '', delay: 2.0 },
|
{ text: '// Rich = has params, returns, env', delay: 2.0 },
|
||||||
{ text: '// Schema + examples included', delay: 2.2 },
|
{ text: 'const score = tier + downloads + stars;', delay: 2.3 },
|
||||||
{ text: 'console.log(tool.schema, tool.examples);', delay: 2.5 },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function TypewriterLine({ text, delay }: { text: string; delay: number }) {
|
function TypewriterLine({ text, delay }: { text: string; delay: number }) {
|
||||||
|
|
@ -40,18 +39,15 @@ function TypewriterLine({ text, delay }: { text: string; delay: number }) {
|
||||||
return () => clearTimeout(timeout);
|
return () => clearTimeout(timeout);
|
||||||
}, [text, delay]);
|
}, [text, delay]);
|
||||||
|
|
||||||
// Syntax highlighting
|
// Simple text display without syntax highlighting to avoid dangerouslySetInnerHTML
|
||||||
const highlightedText = displayText
|
const isComment = displayText.startsWith('//');
|
||||||
.replace(/(import|from|const|await)/g, '<span class="text-purple-400">$1</span>')
|
|
||||||
.replace(/('.*?')/g, '<span class="text-emerald-400">$1</span>')
|
|
||||||
.replace(/(\/\/.*)/g, '<span class="text-white/30">$1</span>')
|
|
||||||
.replace(/(\{|\}|\(|\))/g, '<span class="text-yellow-300">$1</span>');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="font-mono text-sm md:text-base text-white/80 leading-relaxed"
|
className={`font-mono text-sm md:text-base leading-relaxed ${isComment ? 'text-white/30' : 'text-white/80'}`}
|
||||||
dangerouslySetInnerHTML={{ __html: highlightedText || ' ' }}
|
>
|
||||||
/>
|
{displayText || '\u00A0'}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,11 +69,11 @@ export function IntegrationSlide(): React.ReactElement {
|
||||||
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
|
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
|
||||||
className="text-6xl mb-8"
|
className="text-6xl mb-8"
|
||||||
>
|
>
|
||||||
🔌
|
📊
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Immediate Benefits</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Quality Scoring</h2>
|
||||||
<p className="text-xl text-white/40 mb-12">Even if you do nothing else</p>
|
<p className="text-xl text-white/40 mb-12">Deliberately simple. Not trying to be clever.</p>
|
||||||
|
|
||||||
{/* Code block */}
|
{/* Code block */}
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -96,8 +92,8 @@ export function IntegrationSlide(): React.ReactElement {
|
||||||
|
|
||||||
{/* Code content */}
|
{/* Code content */}
|
||||||
<div className="bg-[#1e1e2e] p-6 rounded-b-xl text-left overflow-x-auto">
|
<div className="bg-[#1e1e2e] p-6 rounded-b-xl text-left overflow-x-auto">
|
||||||
{codeLines.map((line, index) => (
|
{codeLines.map((line) => (
|
||||||
<TypewriterLine key={index} text={line.text} delay={line.delay} />
|
<TypewriterLine key={line.text || line.delay} text={line.text} delay={line.delay} />
|
||||||
))}
|
))}
|
||||||
<motion.span
|
<motion.span
|
||||||
animate={{ opacity: [1, 0, 1] }}
|
animate={{ opacity: [1, 0, 1] }}
|
||||||
|
|
@ -107,19 +103,19 @@ export function IntegrationSlide(): React.ReactElement {
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Benefits badges */}
|
{/* Score factors */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ delay: 3 }}
|
transition={{ delay: 3 }}
|
||||||
className="mt-8 flex flex-wrap justify-center gap-3"
|
className="mt-8 flex flex-wrap justify-center gap-3"
|
||||||
>
|
>
|
||||||
{['Less spelunking', 'Schemas + examples', 'Shared vocabulary', 'Tool visibility'].map((benefit) => (
|
{['Tier: 40-60%', 'Downloads: 0-20%', 'Stars: 0-10%', 'Richness: 0-10%'].map((factor) => (
|
||||||
<span
|
<span
|
||||||
key={benefit}
|
key={factor}
|
||||||
className="px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400/70 text-sm"
|
className="px-3 py-1 rounded-full bg-cyan-500/10 border border-cyan-500/20 text-cyan-400/70 text-sm font-mono"
|
||||||
>
|
>
|
||||||
{benefit}
|
{factor}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ export function ProblemSlide(): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||||
{/* Floating chaotic icons */}
|
{/* Floating chaotic icons */}
|
||||||
{floatingIcons.map((item, index) => (
|
{floatingIcons.map((item) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={item.icon}
|
||||||
className="absolute text-4xl md:text-5xl opacity-20"
|
className="absolute text-4xl md:text-5xl opacity-20"
|
||||||
initial={{ opacity: 0, scale: 0 }}
|
initial={{ opacity: 0, scale: 0 }}
|
||||||
animate={{
|
animate={{
|
||||||
|
|
@ -73,13 +73,13 @@ export function ProblemSlide(): React.ReactElement {
|
||||||
😵
|
😵
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">Tool Sprawl</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">The Problem</h2>
|
||||||
|
|
||||||
<p className="text-xl md:text-2xl text-white/60 leading-relaxed max-w-2xl">
|
<p className="text-xl md:text-2xl text-white/60 leading-relaxed max-w-2xl">
|
||||||
In modern agent stacks, tools are the{' '}
|
npm has <span className="text-red-400 font-semibold">2 million packages</span>.
|
||||||
<span className="text-red-400 font-semibold">real product surface area</span>.
|
|
||||||
<br />
|
<br />
|
||||||
But discovery is the <span className="text-orange-400 font-semibold">bottleneck</span>.
|
Which ones are AI-callable tools? Which ones{' '}
|
||||||
|
<span className="text-orange-400 font-semibold">actually work</span>?
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -88,17 +88,19 @@ export function ProblemSlide(): React.ReactElement {
|
||||||
transition={{ delay: 1 }}
|
transition={{ delay: 1 }}
|
||||||
className="mt-12 flex flex-wrap justify-center gap-4"
|
className="mt-12 flex flex-wrap justify-center gap-4"
|
||||||
>
|
>
|
||||||
{['Dozens of packages', 'Missing schemas', 'Vague READMEs', 'Unknown status'].map((word, i) => (
|
{["Can't find them", "Can't trust them", "Can't compare them", 'No schemas'].map(
|
||||||
<motion.span
|
(word, i) => (
|
||||||
key={word}
|
<motion.span
|
||||||
initial={{ opacity: 0, y: 20 }}
|
key={word}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
transition={{ delay: 1.2 + i * 0.1 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
className="px-4 py-2 rounded-full border border-red-500/30 text-red-400/60 text-sm font-mono"
|
transition={{ delay: 1.2 + i * 0.1 }}
|
||||||
>
|
className="px-4 py-2 rounded-full border border-red-500/30 text-red-400/60 text-sm font-mono"
|
||||||
{word}
|
>
|
||||||
</motion.span>
|
{word}
|
||||||
))}
|
</motion.span>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-32 h-32">
|
<div className="relative w-32 h-32">
|
||||||
<svg className="w-full h-full transform -rotate-90">
|
<svg aria-hidden="true" className="w-full h-full transform -rotate-90">
|
||||||
{/* Background circle */}
|
{/* Background circle */}
|
||||||
<circle cx="64" cy="64" r="45" fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="8" />
|
<circle cx="64" cy="64" r="45" fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="8" />
|
||||||
{/* Progress circle */}
|
{/* Progress circle */}
|
||||||
|
|
@ -58,10 +58,10 @@ function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number })
|
||||||
}
|
}
|
||||||
|
|
||||||
const qualityFactors = [
|
const qualityFactors = [
|
||||||
{ name: 'Quality Signals', icon: '📊', score: 95, color: 'cyan', desc: 'Docs, schema, adoption' },
|
{ name: 'Import Health', icon: '📦', score: 95, color: 'cyan', desc: 'Can we require() it?' },
|
||||||
{ name: 'Health Checks', icon: '💚', score: 88, color: 'emerald', desc: 'Imports, exports, runs' },
|
{ name: 'Execution Health', icon: '▶️', score: 88, color: 'emerald', desc: 'Does it run at all?' },
|
||||||
{ name: 'The Playground', icon: '🎮', score: 92, color: 'purple', desc: 'Try before you adopt' },
|
{ name: 'HEALTHY', icon: '✅', score: 100, color: 'emerald', desc: 'Passed both checks' },
|
||||||
{ name: 'Remote Execution', icon: '☁️', score: 85, color: 'yellow', desc: 'Optional sandbox' },
|
{ name: 'BROKEN', icon: '❌', score: 0, color: 'yellow', desc: 'Labeled, not hidden' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function QualitySlide(): React.ReactElement {
|
export function QualitySlide(): React.ReactElement {
|
||||||
|
|
@ -85,8 +85,10 @@ export function QualitySlide(): React.ReactElement {
|
||||||
⭐
|
⭐
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">The Multipliers</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Health Checks</h2>
|
||||||
<p className="text-xl text-white/40 mb-12">Extra leverage when you're ready</p>
|
<p className="text-xl text-white/40 mb-12">
|
||||||
|
We don't hide broken tools. We <span className="text-yellow-400">label them</span>.
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* Main gauge */}
|
{/* Main gauge */}
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ export function SolutionSlide(): React.ReactElement {
|
||||||
✨
|
✨
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">TPMJS</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">What TPMJS Is</h2>
|
||||||
|
|
||||||
<p className="text-xl md:text-2xl text-white/60 leading-relaxed mb-12 max-w-2xl">
|
<p className="text-xl md:text-2xl text-white/60 leading-relaxed mb-12 max-w-2xl">
|
||||||
A registry that indexes AI-tool packages and exposes{' '}
|
A <span className="text-cyan-400 font-semibold">registry</span> that indexes npm packages
|
||||||
<span className="text-cyan-400 font-semibold">normalized, enriched information</span>
|
for AI tool use.
|
||||||
<br />
|
<br />
|
||||||
so agents and engineers can{' '}
|
<span className="text-emerald-400 font-semibold">Extracts schemas from code</span>. Scores
|
||||||
<span className="text-emerald-400 font-semibold">discover the right tool faster</span>.
|
quality. Checks health.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Organized grid of icons */}
|
{/* Organized grid of icons */}
|
||||||
|
|
@ -46,9 +46,9 @@ export function SolutionSlide(): React.ReactElement {
|
||||||
transition={{ delay: 0.5 }}
|
transition={{ delay: 0.5 }}
|
||||||
className="grid grid-cols-4 md:grid-cols-6 gap-4 max-w-lg mx-auto"
|
className="grid grid-cols-4 md:grid-cols-6 gap-4 max-w-lg mx-auto"
|
||||||
>
|
>
|
||||||
{gridItems.map((item, index) => (
|
{gridItems.map((item) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={item.icon}
|
||||||
initial={{ opacity: 0, scale: 0, y: 20 }}
|
initial={{ opacity: 0, scale: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
transition={{
|
transition={{
|
||||||
|
|
@ -70,7 +70,7 @@ export function SolutionSlide(): React.ReactElement {
|
||||||
transition={{ delay: 1.5 }}
|
transition={{ delay: 1.5 }}
|
||||||
className="mt-12 flex flex-wrap justify-center gap-4"
|
className="mt-12 flex flex-wrap justify-center gap-4"
|
||||||
>
|
>
|
||||||
{['Catalog', 'Metadata layer', 'Search surface', 'Consistent contract'].map((word, i) => (
|
{['Registry', 'Schema extraction', 'Quality scoring', 'Health checks'].map((word, i) => (
|
||||||
<motion.span
|
<motion.span
|
||||||
key={word}
|
key={word}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@ export function ToolDetailSlide(): React.ReactElement {
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
className="relative z-10 max-w-4xl w-full"
|
className="relative z-10 max-w-4xl w-full"
|
||||||
>
|
>
|
||||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Tool-Shaped Results</h2>
|
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">What We Store</h2>
|
||||||
<p className="text-xl text-white/40 mb-12">Remove guesswork before you integrate</p>
|
<p className="text-xl text-white/40 mb-12">
|
||||||
|
Everything an agent needs to <span className="text-cyan-400">call a tool correctly</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* Mock tool card */}
|
{/* Mock tool card */}
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -91,15 +93,18 @@ export function ToolDetailSlide(): React.ReactElement {
|
||||||
transition={{ delay: 1 }}
|
transition={{ delay: 1 }}
|
||||||
className="p-4 rounded-lg bg-black/30 font-mono text-sm"
|
className="p-4 rounded-lg bg-black/30 font-mono text-sm"
|
||||||
>
|
>
|
||||||
<div className="text-white/40 mb-2">// Input Schema</div>
|
<div className="text-white/40 mb-2">{'// Extracted from actual code'}</div>
|
||||||
<div className="text-cyan-400">
|
<div className="text-purple-400">
|
||||||
query: <span className="text-white/60">string</span>
|
inputSchema: <span className="text-white/60">JSON Schema</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-cyan-400">
|
<div className="text-purple-400">
|
||||||
limit?: <span className="text-white/60">number</span>
|
returnSchema: <span className="text-white/60">JSON Schema</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-cyan-400">
|
<div className="text-purple-400">
|
||||||
domains?: <span className="text-white/60">string[]</span>
|
envKeys: <span className="text-white/60">["API_KEY"]</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-purple-400">
|
||||||
|
tier: <span className="text-emerald-400">"rich"</span>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ export function WelcomeSlide(): React.ReactElement {
|
||||||
transition={{ duration: 0.8, delay: 0.5 }}
|
transition={{ duration: 0.8, delay: 0.5 }}
|
||||||
className="mt-6 text-2xl md:text-3xl text-white/60 font-light max-w-2xl"
|
className="mt-6 text-2xl md:text-3xl text-white/60 font-light max-w-2xl"
|
||||||
>
|
>
|
||||||
Tool Discovery for AI Agents
|
The missing layer between "LLMs can call tools" and "which tool,
|
||||||
|
exactly?"
|
||||||
</motion.p>
|
</motion.p>
|
||||||
|
|
||||||
{/* Decorative line */}
|
{/* Decorative line */}
|
||||||
|
|
@ -79,7 +80,13 @@ export function WelcomeSlide(): React.ReactElement {
|
||||||
transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }}
|
transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }}
|
||||||
className="text-white/40"
|
className="text-white/40"
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="w-6 h-6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue