feat: add resend tools, MCP collection endpoint, and UI refinements
- Add @tpmjs/tools-resend with email API tools and blocks.yml entries - Add MCP route for collection skill discovery - Add InstallationSection component for collections - Refactor collection pages to use shared components and simplify layouts - Update skills questions API, rate limiting, and API key handling - Add tpmjs-tool-creator skill for Claude - Update video feature scenes and fix lint issues - Update .gitignore with IDE and temp file exclusions
This commit is contained in:
parent
f3a46045ba
commit
a52d32c367
40 changed files with 3310 additions and 1694 deletions
290
.claude/skills/tpmjs-tool-creator/SKILL.md
Normal file
290
.claude/skills/tpmjs-tool-creator/SKILL.md
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
---
|
||||
name: tpmjs-tool-creator
|
||||
description: Guide for creating official TPMJS tools using the blocks CLI. Use when a user wants to create a new tool for the TPMJS registry, add a tool to packages/tools/official/, implement an AI SDK v6 tool, define a block in blocks.yml, validate a tool with `pnpm blocks run`, or publish a tool to npm with the tpmjs keyword.
|
||||
---
|
||||
|
||||
# TPMJS Tool Creator
|
||||
|
||||
Create production-ready tools for the TPMJS registry using the blocks CLI. Tools are npm packages following the AI SDK v6 pattern, validated by blocks, and automatically synced to tpmjs.com.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Define the tool block in `packages/tools/official/blocks.yml`
|
||||
2. Create the tool package directory
|
||||
3. Implement the tool using AI SDK v6 `tool()` + `jsonSchema()`
|
||||
4. Validate with `pnpm blocks run <tool-name>`
|
||||
5. Build and publish to npm
|
||||
|
||||
## Step 1: Define in blocks.yml
|
||||
|
||||
Add to the `blocks:` section of `packages/tools/official/blocks.yml`:
|
||||
|
||||
```yaml
|
||||
blocks:
|
||||
category.toolName:
|
||||
type: utility
|
||||
description: "LLM-friendly description of what the tool does"
|
||||
path: "tool-directory-name"
|
||||
domain_rules:
|
||||
- id: rule_name
|
||||
description: "What this implementation must do"
|
||||
inputs:
|
||||
- name: inputName
|
||||
type: string
|
||||
description: "Description for LLMs"
|
||||
- name: optionalInput
|
||||
type: number
|
||||
optional: true
|
||||
description: "Optional parameter"
|
||||
outputs:
|
||||
- name: result
|
||||
type: ResultType
|
||||
description: "What the tool returns"
|
||||
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
|
||||
```
|
||||
|
||||
**Category prefix** (before the dot): `research`, `web`, `data`, `documentation`, `engineering`, `security`, `statistics`, `ops`, `agent`, `sandbox`, `utilities`, `html`, `compliance`.
|
||||
|
||||
For domain entities and quality measures, see [references/domain.md](references/domain.md).
|
||||
|
||||
## Step 2: Create Package Directory
|
||||
|
||||
Create `packages/tools/official/<tool-name>/`:
|
||||
|
||||
```
|
||||
<tool-name>/
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── tsup.config.ts
|
||||
├── README.md
|
||||
└── src/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
**package.json:**
|
||||
```json
|
||||
{
|
||||
"name": "@tpmjs/official-<tool-name>",
|
||||
"version": "0.1.0",
|
||||
"description": "Short description",
|
||||
"type": "module",
|
||||
"keywords": ["tpmjs", "<category>", "ai"],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"type-check": "tsc --noEmit",
|
||||
"clean": "rm -rf dist .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tpmjs/tsconfig": "workspace:*",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ai": "6.0.49"
|
||||
},
|
||||
"publishConfig": { "access": "public" },
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tpmjs/tpmjs.git",
|
||||
"directory": "packages/tools/official/<tool-name>"
|
||||
},
|
||||
"homepage": "https://tpmjs.com",
|
||||
"license": "MIT",
|
||||
"tpmjs": {
|
||||
"category": "<category>",
|
||||
"frameworks": ["vercel-ai"],
|
||||
"tools": [
|
||||
{
|
||||
"name": "toolName",
|
||||
"description": "Clear description (20+ chars)."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**tsconfig.json:**
|
||||
```json
|
||||
{
|
||||
"extends": "@tpmjs/tsconfig/react-library.json",
|
||||
"compilerOptions": { "outDir": "dist", "rootDir": "src" },
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
```
|
||||
|
||||
**tsup.config.ts:**
|
||||
```typescript
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
sourcemap: true,
|
||||
target: 'es2022',
|
||||
});
|
||||
```
|
||||
|
||||
## Step 3: Implement the Tool
|
||||
|
||||
Every tool follows this AI SDK v6 pattern in `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { jsonSchema, tool } from 'ai';
|
||||
|
||||
interface MyToolInput {
|
||||
param1: string;
|
||||
param2?: number;
|
||||
}
|
||||
|
||||
export interface MyToolResult {
|
||||
data: string;
|
||||
metadata: { processedAt: string };
|
||||
}
|
||||
|
||||
export const myTool = tool({
|
||||
description: 'Clear LLM-friendly description of what this tool does.',
|
||||
parameters: jsonSchema<MyToolInput>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
param1: {
|
||||
type: 'string',
|
||||
description: 'What param1 is for',
|
||||
},
|
||||
param2: {
|
||||
type: 'number',
|
||||
description: 'Optional: what param2 is for',
|
||||
},
|
||||
},
|
||||
required: ['param1'],
|
||||
additionalProperties: false,
|
||||
}),
|
||||
execute: async (input): Promise<MyToolResult> => {
|
||||
if (!input.param1) {
|
||||
throw new Error('param1 is required and must be non-empty');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await processData(input.param1);
|
||||
return {
|
||||
data: result,
|
||||
metadata: { processedAt: new Date().toISOString() },
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to process: ${error instanceof Error ? error.message : String(error)}`
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default myTool;
|
||||
```
|
||||
|
||||
**Hard rules:**
|
||||
- No stubs, TODOs, or placeholders — every tool must be fully working
|
||||
- Single-shot: one call in, one structured result out
|
||||
- Validate inputs before processing
|
||||
- Try-catch with descriptive errors including context
|
||||
- `additionalProperties: false` on jsonSchema
|
||||
- Description on every schema property
|
||||
- Export as both named and default export
|
||||
- Output interface must be exported
|
||||
|
||||
### Multi-Tool Packages
|
||||
|
||||
For packages with multiple tools, add root-level files:
|
||||
|
||||
**block.ts:**
|
||||
```typescript
|
||||
import { toolA, toolB } from './src/index.js';
|
||||
export const block = { name: 'package-name', tools: { toolA, toolB } };
|
||||
export default block;
|
||||
```
|
||||
|
||||
**index.ts (root):**
|
||||
```typescript
|
||||
export * from './src/index.js';
|
||||
export { default } from './src/index.js';
|
||||
```
|
||||
|
||||
Each tool gets its own entry in blocks.yml (same `path`) and in `tpmjs.tools` array.
|
||||
|
||||
## Step 4: Validate
|
||||
|
||||
```bash
|
||||
cd packages/tools/official
|
||||
|
||||
pnpm blocks run <tool-name> # Validate (schema → shape → domain)
|
||||
pnpm blocks run <tool-name> --force # Force full validation (skip cache)
|
||||
pnpm blocks run <tool-name> --json # JSON output for debugging
|
||||
pnpm blocks run --all # Validate all tools
|
||||
```
|
||||
|
||||
**Common errors:**
|
||||
- `Tool "X" not found in exports` → Export name must match blocks.yml
|
||||
- `Required file not found` → Check package root has all required files
|
||||
- `invalid tpmjs field` → Category must be valid, tools array required
|
||||
|
||||
## Step 5: Build and Publish
|
||||
|
||||
```bash
|
||||
pnpm --filter=@tpmjs/official-<tool-name> build
|
||||
cd packages/tools/official/<tool-name> && npm publish --access public
|
||||
```
|
||||
|
||||
The tool syncs to tpmjs.com automatically via the changes feed (every 2 min) and keyword search (every 15 min). To trigger immediately:
|
||||
|
||||
```bash
|
||||
source apps/web/.env.local
|
||||
curl -X POST https://tpmjs.com/api/sync/keyword \
|
||||
-H "Authorization: Bearer $CRON_SECRET"
|
||||
```
|
||||
|
||||
## README Template
|
||||
|
||||
Every tool needs a README:
|
||||
|
||||
```markdown
|
||||
# @tpmjs/official-<tool-name>
|
||||
|
||||
Short description.
|
||||
|
||||
## Installation
|
||||
|
||||
npm install @tpmjs/official-<tool-name>
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`typescript
|
||||
import { myTool } from '@tpmjs/official-<tool-name>';
|
||||
|
||||
const result = await myTool.execute({ param1: 'example' });
|
||||
\`\`\`
|
||||
|
||||
## Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|--------|--------|----------|--------------------|
|
||||
| param1 | string | Yes | What param1 is for |
|
||||
|
||||
## Output
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|--------|----------------------|
|
||||
| data | string | The processed result |
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
```
|
||||
58
.claude/skills/tpmjs-tool-creator/references/domain.md
Normal file
58
.claude/skills/tpmjs-tool-creator/references/domain.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Domain Reference
|
||||
|
||||
## Entities
|
||||
|
||||
Reusable output types defined in blocks.yml. Reference these in your tool's output `type` field.
|
||||
|
||||
| Entity | Fields |
|
||||
|--------|--------|
|
||||
| url | href, domain, protocol, path, query, fragment |
|
||||
| webpage | url, title, html, text, metadata |
|
||||
| text_content | raw, sentences, paragraphs, wordCount |
|
||||
| claim | statement, confidence, needsCitation, category |
|
||||
| timeline | events, dateRange, gaps, eventCount |
|
||||
| evidence | source, type, strength, relevance |
|
||||
| summary | text, keyPoints, length, compressionRatio |
|
||||
| sentiment | score, label, confidence, aspects |
|
||||
| entity | name, type, mentions, context |
|
||||
| relationship | source, target, type, strength |
|
||||
| pattern | name, frequency, examples, significance |
|
||||
| anomaly | description, severity, context, recommendation |
|
||||
| metric | name, value, unit, trend |
|
||||
| comparison | items, criteria, rankings, analysis |
|
||||
| recommendation | action, priority, rationale, impact |
|
||||
| risk | description, likelihood, impact, mitigation |
|
||||
| code_snippet | language, code, explanation, complexity |
|
||||
| api_endpoint | method, path, parameters, response |
|
||||
| data_schema | fields, types, constraints, relationships |
|
||||
| workflow_step | action, input, output, conditions |
|
||||
|
||||
## Quality Measures
|
||||
|
||||
Reference these in your output's `measures` array.
|
||||
|
||||
| Measure | Severity | What it checks |
|
||||
|---------|----------|---------------|
|
||||
| working_implementation | error | No TODOs, stubs, or placeholders. Returns actual computed values. |
|
||||
| valid_output_structure | error | Returns object matching declared interface. All required fields present. Arrays never undefined. |
|
||||
| proper_error_handling | error | Throws descriptive Error with context. Validates inputs. Catches external API errors. |
|
||||
| ai_sdk_compliance | error | Uses `tool()` + `jsonSchema()` from 'ai'. Clear description. Every property has description. |
|
||||
| npm_publishable | error | Valid package.json with tpmjs field. Named + default exports. Proper types. Semver version. |
|
||||
| readme_documentation | error | README exists. Describes tool. Usage example. Documents inputs/outputs. |
|
||||
| deterministic_output | warning | Same input produces same output (where applicable). |
|
||||
| minimal_dependencies | warning | Uses stable, well-maintained packages. Avoids unnecessary deps. |
|
||||
|
||||
## Domain Rules
|
||||
|
||||
Common domain rule categories for the `domain_rules` field in blocks.yml:
|
||||
|
||||
- **Core implementation**: working code, proper types, error handling
|
||||
- **Web & fetch**: URL validation, content extraction, timeout handling
|
||||
- **Document generation**: format compliance, template rendering
|
||||
- **Data transformation**: schema validation, type coercion, encoding
|
||||
- **Engineering/code analysis**: AST parsing, complexity metrics
|
||||
- **Security & compliance**: input sanitization, safe execution
|
||||
- **Statistical rigor**: numerical accuracy, proper rounding
|
||||
- **Workflow/recipe**: step sequencing, state management
|
||||
|
||||
Define custom rules specific to your tool's requirements. Each rule needs an `id` and `description`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue