From 5e795bc08699985aa0bfd96117caa8c28e9336e4 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Wed, 17 Dec 2025 16:20:20 +1000 Subject: [PATCH] fix: require name field in TPMJS spec, add workflow permissions Breaking change for TPMJS spec: - Remove `exportName` field support from TpmjsToolDefinitionSchema - Make `name` field required (replaces deprecated `exportName`) - Update create-basic-tools to generate `name` field - Update sync routes to use `name` from validated schema - Add `permissions: contents: write` to Vercel registry sync workflow Packages using the old `exportName` field must update to use `name`. --- .github/workflows/sync-vercel-registry.yml | 3 ++ apps/web/src/app/api/sync/changes/route.ts | 9 ++--- apps/web/src/app/api/sync/keyword/route.ts | 9 ++--- .../src/generators/package-json.ts | 5 +-- packages/types/src/tpmjs.ts | 34 +++++++------------ 5 files changed, 22 insertions(+), 38 deletions(-) diff --git a/.github/workflows/sync-vercel-registry.yml b/.github/workflows/sync-vercel-registry.yml index ad3bf15..af21029 100644 --- a/.github/workflows/sync-vercel-registry.yml +++ b/.github/workflows/sync-vercel-registry.yml @@ -12,6 +12,9 @@ on: paths: - 'sync-vercel-registry.ts' +permissions: + contents: write + jobs: sync-vercel: runs-on: ubuntu-latest diff --git a/apps/web/src/app/api/sync/changes/route.ts b/apps/web/src/app/api/sync/changes/route.ts index 54f97dc..bbe97f2 100644 --- a/apps/web/src/app/api/sync/changes/route.ts +++ b/apps/web/src/app/api/sync/changes/route.ts @@ -167,8 +167,8 @@ export async function POST(request: NextRequest) { // Upsert each tool for (const toolDef of toolsToProcess) { - // Use 'name' field (new) or fall back to 'exportName' (legacy support) - const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName; + // Get tool name from validated schema + const toolName = toolDef.name; if (!toolName) { console.warn(`Skipping tool without name in ${pkg.name}`); continue; @@ -255,10 +255,7 @@ export async function POST(request: NextRequest) { // Delete orphaned tools (tools removed from package.json) const orphanedTools = existingTools.filter( (existingTool) => - !toolsToProcess.some((toolDef) => { - const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName; - return toolName === existingTool.exportName; - }) + !toolsToProcess.some((toolDef) => toolDef.name === existingTool.exportName) ); if (orphanedTools.length > 0) { diff --git a/apps/web/src/app/api/sync/keyword/route.ts b/apps/web/src/app/api/sync/keyword/route.ts index 20b7f6e..902e17b 100644 --- a/apps/web/src/app/api/sync/keyword/route.ts +++ b/apps/web/src/app/api/sync/keyword/route.ts @@ -186,8 +186,8 @@ export async function POST(request: NextRequest) { // Upsert each tool for (const toolDef of toolsToProcess) { - // Use 'name' field (new) or fall back to 'exportName' (legacy support) - const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName; + // Get tool name from validated schema + const toolName = toolDef.name; if (!toolName) { console.warn(`Skipping tool without name in ${pkg.name}`); continue; @@ -274,10 +274,7 @@ export async function POST(request: NextRequest) { // Delete orphaned tools (tools removed from package.json) const orphanedTools = existingTools.filter( (existingTool) => - !toolsToProcess.some((toolDef) => { - const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName; - return toolName === existingTool.exportName; - }) + !toolsToProcess.some((toolDef) => toolDef.name === existingTool.exportName) ); if (orphanedTools.length > 0) { diff --git a/packages/tools/create-basic-tools/src/generators/package-json.ts b/packages/tools/create-basic-tools/src/generators/package-json.ts index 3bea8bf..deb417f 100644 --- a/packages/tools/create-basic-tools/src/generators/package-json.ts +++ b/packages/tools/create-basic-tools/src/generators/package-json.ts @@ -24,11 +24,8 @@ export function generatePackageJson(config: GeneratorConfig): string { tpmjs: { category: packageInfo.category, tools: tools.map((tool) => ({ - exportName: tool.exportName, + name: tool.exportName, description: tool.description, - ...(tool.parameters && { parameters: tool.parameters }), - ...(tool.returns && { returns: tool.returns }), - ...(tool.aiAgent && { aiAgent: tool.aiAgent }), })), ...(tools.some((t) => t.env) && { env: tools.flatMap((t) => t.env || []), diff --git a/packages/types/src/tpmjs.ts b/packages/types/src/tpmjs.ts index 148ca17..2b13db7 100644 --- a/packages/types/src/tpmjs.ts +++ b/packages/types/src/tpmjs.ts @@ -90,28 +90,18 @@ export type TpmjsAiAgent = z.infer; * - returns: Tool return type - auto-extracted from tool * - aiAgent: AI agent guidance - auto-extracted from tool */ -export const TpmjsToolDefinitionSchema = z - .object({ - // New field name (preferred) - name: z.string().min(1).optional(), - // @deprecated - use 'name' instead. Kept for backward compatibility. - exportName: z.string().min(1).optional(), - // Optional - auto-extracted from tool if not provided - description: z - .string() - .min(20, 'Description must be at least 20 characters') - .max(500) - .optional(), - // @deprecated - now auto-extracted from tool's inputSchema - parameters: z.array(TpmjsParameterSchema).optional(), - // @deprecated - now auto-extracted from tool - returns: TpmjsReturnsSchema.optional(), - // @deprecated - now auto-extracted from tool - aiAgent: TpmjsAiAgentSchema.optional(), - }) - .refine((data) => data.name || data.exportName, { - message: 'Either name or exportName is required', - }); +export const TpmjsToolDefinitionSchema = z.object({ + // Required: The export name of the tool from the package + name: z.string().min(1), + // Optional - auto-extracted from tool if not provided + description: z.string().min(20, 'Description must be at least 20 characters').max(500).optional(), + // @deprecated - now auto-extracted from tool's inputSchema + parameters: z.array(TpmjsParameterSchema).optional(), + // @deprecated - now auto-extracted from tool + returns: TpmjsReturnsSchema.optional(), + // @deprecated - now auto-extracted from tool + aiAgent: TpmjsAiAgentSchema.optional(), +}); export type TpmjsToolDefinition = z.infer;