diff --git a/src/shared/toolNameValidation.ts b/src/shared/toolNameValidation.ts index fa96afde0..b770bdd2a 100644 --- a/src/shared/toolNameValidation.ts +++ b/src/shared/toolNameValidation.ts @@ -1,17 +1,17 @@ /** * Tool name validation utilities according to SEP: Specify Format for Tool Names * - * Tool names SHOULD be between 1 and 128 characters in length (inclusive). + * Tool names SHOULD be between 1 and 64 characters in length (inclusive). * Tool names are case-sensitive. * Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits - * (0-9), underscore (_), dash (-), and dot (.). + * (0-9), underscore (_), dash (-), dot (.), and forward slash (/). * Tool names SHOULD NOT contain spaces, commas, or other special characters. */ /** * Regular expression for valid tool names according to SEP-986 specification */ -const TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/; +const TOOL_NAME_REGEX = /^[A-Za-z0-9._/-]{1,64}$/; /** * Validates a tool name according to the SEP specification @@ -32,10 +32,10 @@ export function validateToolName(name: string): { }; } - if (name.length > 128) { + if (name.length > 64) { return { isValid: false, - warnings: [`Tool name exceeds maximum length of 128 characters (current: ${name.length})`] + warnings: [`Tool name exceeds maximum length of 64 characters (current: ${name.length})`] }; } @@ -57,16 +57,20 @@ export function validateToolName(name: string): { warnings.push('Tool name starts or ends with a dot, which may cause parsing issues in some contexts'); } + if (name.startsWith('/') || name.endsWith('/')) { + warnings.push('Tool name starts or ends with a forward slash, which may cause parsing issues in some contexts'); + } + // Check for invalid characters if (!TOOL_NAME_REGEX.test(name)) { const invalidChars = name .split('') - .filter(char => !/[A-Za-z0-9._-]/.test(char)) + .filter(char => !/[A-Za-z0-9._/-]/.test(char)) .filter((char, index, arr) => arr.indexOf(char) === index); // Remove duplicates warnings.push( `Tool name contains invalid characters: ${invalidChars.map(c => `"${c}"`).join(', ')}`, - 'Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)' + 'Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), dot (.), and forward slash (/)' ); return { diff --git a/test/shared/toolNameValidation.test.ts b/test/shared/toolNameValidation.test.ts index bd3c5ea4f..66adb7fbd 100644 --- a/test/shared/toolNameValidation.test.ts +++ b/test/shared/toolNameValidation.test.ts @@ -15,14 +15,15 @@ afterEach(() => { describe('validateToolName', () => { describe('valid tool names', () => { test.each` - description | toolName - ${'simple alphanumeric names'} | ${'getUser'} - ${'names with underscores'} | ${'get_user_profile'} - ${'names with dashes'} | ${'user-profile-update'} - ${'names with dots'} | ${'admin.tools.list'} - ${'mixed character names'} | ${'DATA_EXPORT_v2.1'} - ${'single character names'} | ${'a'} - ${'128 character names'} | ${'a'.repeat(128)} + description | toolName + ${'simple alphanumeric names'} | ${'getUser'} + ${'names with underscores'} | ${'get_user_profile'} + ${'names with dashes'} | ${'user-profile-update'} + ${'names with dots'} | ${'admin.tools.list'} + ${'mixed character names'} | ${'DATA_EXPORT_v2.1'} + ${'single character names'} | ${'a'} + ${'64 character names'} | ${'a'.repeat(64)} + ${'names with forward slashes'} | ${'user/profile/update'} `('should accept $description', ({ toolName }) => { const result = validateToolName(toolName); expect(result.isValid).toBe(true); @@ -34,10 +35,9 @@ describe('validateToolName', () => { test.each` description | toolName | expectedWarning ${'empty names'} | ${''} | ${'Tool name cannot be empty'} - ${'names longer than 128 characters'} | ${'a'.repeat(129)} | ${'Tool name exceeds maximum length of 128 characters (current: 129)'} + ${'names longer than 64 characters'} | ${'a'.repeat(65)} | ${'Tool name exceeds maximum length of 64 characters (current: 65)'} ${'names with spaces'} | ${'get user profile'} | ${'Tool name contains invalid characters: " "'} ${'names with commas'} | ${'get,user,profile'} | ${'Tool name contains invalid characters: ","'} - ${'names with forward slashes'} | ${'user/profile/update'} | ${'Tool name contains invalid characters: "/"'} ${'names with other special chars'} | ${'user@domain.com'} | ${'Tool name contains invalid characters: "@"'} ${'names with multiple invalid chars'} | ${'user name@domain,com'} | ${'Tool name contains invalid characters: " ", "@", ","'} ${'names with unicode characters'} | ${'user-ñame'} | ${'Tool name contains invalid characters: "ñ"'} @@ -94,7 +94,7 @@ describe('validateAndWarnToolName', () => { ${'completely valid names'} | ${'get-user-profile'} | ${true} | ${false} ${'invalid names with spaces'} | ${'get user profile'} | ${false} | ${true} ${'empty names'} | ${''} | ${false} | ${true} - ${'names exceeding length limit'} | ${'a'.repeat(129)} | ${false} | ${true} + ${'names exceeding length limit'} | ${'a'.repeat(65)} | ${false} | ${true} `('should handle $description', ({ toolName, expectedResult, shouldWarn }) => { const result = validateAndWarnToolName(toolName); expect(result).toBe(expectedResult); @@ -118,7 +118,7 @@ describe('edge cases and robustness', () => { description | toolName | shouldBeValid | expectedWarning ${'names with only dots'} | ${'...'} | ${true} | ${'Tool name starts or ends with a dot, which may cause parsing issues in some contexts'} ${'names with only dashes'} | ${'---'} | ${true} | ${'Tool name starts or ends with a dash, which may cause parsing issues in some contexts'} - ${'names with only forward slashes'} | ${'///'} | ${false} | ${'Tool name contains invalid characters: "/"'} + ${'names with only forward slashes'} | ${'///'} | ${true} | ${'Tool name starts or ends with a forward slash, which may cause parsing issues in some contexts'} ${'names with mixed valid/invalid chars'} | ${'user@name123'} | ${false} | ${'Tool name contains invalid characters: "@"'} `('should handle $description', ({ toolName, shouldBeValid, expectedWarning }) => { const result = validateToolName(toolName);