From 5cdf5962ff2bd70bcd2a399310c184bc37554dd9 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Wed, 2 Jul 2025 11:14:38 -0500 Subject: [PATCH] fix: relax tool name validation a bit --- .../src/app/marketplace/marketplace-index.tsx | 17 +++++++++++++++++ packages/types/src/tools.ts | 12 +++++++++++- .../validators/src/parse-tool-identifier.ts | 6 +++--- packages/validators/src/validators.test.ts | 5 +++-- packages/validators/src/validators.ts | 2 +- pnpm-lock.yaml | 6 ++++++ 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/marketplace/marketplace-index.tsx b/apps/web/src/app/marketplace/marketplace-index.tsx index 75968ae2..04141b34 100644 --- a/apps/web/src/app/marketplace/marketplace-index.tsx +++ b/apps/web/src/app/marketplace/marketplace-index.tsx @@ -3,9 +3,11 @@ import useInfiniteScroll from 'react-infinite-scroll-hook' import { useAgentic } from '@/components/agentic-provider' +import { DotsSection } from '@/components/dots-section' import { LoadingIndicator } from '@/components/loading-indicator' import { PageContainer } from '@/components/page-container' import { PublicProject } from '@/components/public-project' +import { SupplySideCTA } from '@/components/supply-side-cta' import { useInfiniteQuery } from '@/lib/query-client' export function MarketplaceIndex() { @@ -90,6 +92,21 @@ export function MarketplaceIndex() { )} + + {/* CTA section */} + +

+ Your API → Paid MCP, Instantly +

+ +
+ Run one command to turn any MCP server or OpenAPI service into a paid + MCP product. With built-in support for every major LLM SDK and MCP + client. +
+ + +
) } diff --git a/packages/types/src/tools.ts b/packages/types/src/tools.ts index 51d904dc..88406a9d 100644 --- a/packages/types/src/tools.ts +++ b/packages/types/src/tools.ts @@ -1,4 +1,8 @@ -import { isToolNameAllowed, toolNameRe } from '@agentic/platform-validators' +import { + isToolNameAllowed, + isValidToolName, + toolNameRe +} from '@agentic/platform-validators' import { z } from '@hono/zod-openapi' import { pricingPlanSlugSchema } from './pricing' @@ -17,6 +21,12 @@ export const toolNameSchema = z .string() .nonempty() .regex(toolNameRe) + .refine( + (name) => isValidToolName(name), + (name) => ({ + message: `Tool name "${name}" is invalid; must match /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/.` + }) + ) .refine( (name) => isToolNameAllowed(name), (name) => ({ diff --git a/packages/validators/src/parse-tool-identifier.ts b/packages/validators/src/parse-tool-identifier.ts index 512f64f9..95a2c557 100644 --- a/packages/validators/src/parse-tool-identifier.ts +++ b/packages/validators/src/parse-tool-identifier.ts @@ -4,13 +4,13 @@ import type { ParsedToolIdentifier, ParseIdentifierOptions } from './types' import { coerceIdentifier } from './utils' const toolIdentifierImplicitRe = - /^@([a-z0-9-]{1,256})\/([a-z0-9-]{1,256})\/([a-zA-Z_][a-zA-Z0-9_]{0,63})$/ + /^@([a-z0-9-]{1,256})\/([a-z0-9-]{1,256})\/([a-zA-Z_][a-zA-Z0-9_-]{0,63})$/ const toolIdentifierHashRe = - /^@([a-z0-9-]{1,256})\/([a-z0-9-]{1,256})@([a-z0-9]{8})\/([a-zA-Z_][a-zA-Z0-9_]{0,63})$/ + /^@([a-z0-9-]{1,256})\/([a-z0-9-]{1,256})@([a-z0-9]{8})\/([a-zA-Z_][a-zA-Z0-9_-]{0,63})$/ const toolIdentifierVersionRe = - /^@([a-z0-9-]{1,256})\/([a-z0-9-]{1,256})@([\d.a-z-@]+)\/([a-zA-Z_][a-zA-Z0-9_]{0,63})$/ + /^@([a-z0-9-]{1,256})\/([a-z0-9-]{1,256})@([\d.a-z-@]+)\/([a-zA-Z_][a-zA-Z0-9_-]{0,63})$/ export function parseToolIdentifier( identifier?: string, diff --git a/packages/validators/src/validators.test.ts b/packages/validators/src/validators.test.ts index 774c2761..2b8b7516 100644 --- a/packages/validators/src/validators.test.ts +++ b/packages/validators/src/validators.test.ts @@ -163,14 +163,15 @@ test('isValidToolName success', () => { expect(isValidToolName('searchGoogle')).toBe(true) expect(isValidToolName('searchGoogle2')).toBe(true) expect(isValidToolName('_searchGoogle')).toBe(true) + expect(isValidToolName('tool-name')).toBe(true) }) test('isValidToolName failure', () => { expect(isValidToolName('ab1.2')).toBe(false) - expect(isValidToolName('foo-bar')).toBe(false) + expect(isValidToolName('-foo-bar')).toBe(false) expect(isValidToolName('abc/123')).toBe(false) expect(isValidToolName('search_google ')).toBe(false) - expect(isValidToolName('search-google')).toBe(false) + expect(isValidToolName('-search_google')).toBe(false) expect( isValidToolName( 'too_long_too_long_too_long_too_long_too_long_too_long_too_long_to' diff --git a/packages/validators/src/validators.ts b/packages/validators/src/validators.ts index 55b24782..05d59a31 100644 --- a/packages/validators/src/validators.ts +++ b/packages/validators/src/validators.ts @@ -13,7 +13,7 @@ export const passwordRe = /^.{3,1024}$/ export const projectSlugRe = /^[a-z0-9-]{1,256}$/ export const deploymentHashRe = /^[a-z0-9]{8}$/ -export const toolNameRe = /^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/ +export const toolNameRe = /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/ export function isValidEmail(value: string): boolean { return emailValidator.validate(value) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec18608f..6a993e38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -964,6 +964,12 @@ importers: specifier: 'catalog:' version: 4.1.97(@types/node@24.0.7)(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) + examples/mcp-servers/context7: + dependencies: + '@agentic/platform': + specifier: workspace:* + version: link:../../../packages/platform + examples/mcp-servers/search: dependencies: '@agentic/platform':