Skip to main content
Glama
tools.test.ts9.43 kB
import { describe, it, expect, vi, assert } from 'vitest'; import { getAvailableTools, validatePatterns } from '../../src/utils/tools.js'; import { Tool } from '../../src/utils/types.js'; import { log } from '../../src/utils/logger.js'; // Mock external dependencies vi.mock('../../src/utils/logger.js', () => ({ log: vi.fn(), logError: vi.fn(), })); describe('Tool Utilities', () => { // Common test fixtures const testTools: Tool[] = [ { name: 'auth0_list_applications', description: 'List all applications', _meta: { requiredScopes: ['read:clients'], readOnly: true, }, }, { name: 'auth0_get_application', description: 'Get application details', _meta: { requiredScopes: ['read:clients'], readOnly: true, }, }, { name: 'auth0_create_application', description: 'Create application', _meta: { requiredScopes: ['create:clients'], }, }, { name: 'auth0_update_application', description: 'Update application', _meta: { requiredScopes: ['update:clients'], }, }, { name: 'auth0_list_resource_servers', description: 'List resource servers', _meta: { requiredScopes: ['read:resource_servers'], readOnly: true, }, }, { name: 'auth0_get_resource_server', description: 'Get resource server details', _meta: { requiredScopes: ['read:resource_servers'], readOnly: true, }, }, ]; describe('validatePatterns function', () => { it('should not throw for valid tool names', () => { expect(() => validatePatterns(['auth0_list_applications'], testTools)).not.toThrow(); }); it('should not throw for valid glob patterns', () => { expect(() => validatePatterns(['auth0_*'], testTools)).not.toThrow(); }); it('should not throw for multiple valid patterns', () => { expect(() => validatePatterns(['auth0_list_*', 'auth0_get_*'], testTools)).not.toThrow(); }); it('should validate each pattern in the array', () => { expect(() => validatePatterns(['auth0_list_applications', 'nonexistent_tool'], testTools) ).toThrow(/Invalid tool: nonexistent_tool/); }); it("should throw for tool names that don't exist", () => { expect(() => validatePatterns(['nonexistent_tool'], testTools)).toThrow( /Invalid tool: nonexistent_tool/ ); }); it("should throw for glob patterns that don't match any tools", () => { expect(() => validatePatterns(['xyz_*_tool'], testTools)).toThrow( /No tools match the pattern/ ); }); it('should list valid tools in the error message for invalid tool names', () => { try { validatePatterns(['nonexistent_tool'], testTools); assert.fail('Expected an error to be thrown'); } catch (error: any) { // Error should include available tool names for user guidance expect(error.message).toContain('auth0_list_applications'); expect(error.message).toContain('auth0_get_application'); } }); it('should handle empty pattern array', () => { expect(() => validatePatterns([], testTools)).not.toThrow(); }); it('should handle patterns with special regex characters', () => { expect(() => validatePatterns(['auth0_list_applications+'], testTools)).toThrow( /Invalid tool/ ); expect(() => validatePatterns(['auth0_[list]_applications'], testTools)).toThrow( /Invalid tool/ ); }); it('should throw appropriate error for null or undefined tools array', () => { expect(() => validatePatterns(['auth0_list_applications'], null as any)).toThrow(); expect(() => validatePatterns(['auth0_list_applications'], undefined as any)).toThrow(); }); it('should throw with descriptive message when no tools are provided', () => { expect(() => validatePatterns(['auth0_list_applications'], [])).toThrow( /no tools available/i ); }); }); describe('getAvailableTools function', () => { it('should return all tools when no patterns are provided', () => { const result = getAvailableTools(testTools); expect(result).toEqual(testTools); }); it('should return all tools when patterns is an empty array', () => { const result = getAvailableTools(testTools, []); expect(result).toEqual(testTools); }); it('should return all tools when the special "*" selector is specified', () => { const result = getAvailableTools(testTools, ['*']); expect(result).toEqual(testTools); }); it('should filter tools by exact name match', () => { const result = getAvailableTools(testTools, ['auth0_list_applications']); expect(result).toHaveLength(1); expect(result[0].name).toBe('auth0_list_applications'); }); it('should filter tools by multiple specific names', () => { const result = getAvailableTools(testTools, [ 'auth0_list_applications', 'auth0_get_application', ]); expect(result).toHaveLength(2); expect(result.map((t) => t.name)).toContain('auth0_list_applications'); expect(result.map((t) => t.name)).toContain('auth0_get_application'); }); it('should filter tools by glob pattern', () => { const result = getAvailableTools(testTools, ['auth0_*_application']); expect(result.length).toBeGreaterThan(0); expect( result.every((t) => t.name.startsWith('auth0_') && t.name.endsWith('_application')) ).toBe(true); }); it('should handle multiple glob patterns correctly', () => { const result = getAvailableTools(testTools, ['auth0_get_*', '*_resource_*']); // Verify results contain matches for both patterns const hasGetTools = result.some((t) => t.name.startsWith('auth0_get_')); const hasResourceTools = result.some((t) => t.name.includes('_resource_')); expect(hasGetTools).toBe(true); expect(hasResourceTools).toBe(true); }); it('should handle tools with whitespace in names', () => { const toolWithSpace = { name: 'tool with space', description: 'Test tool', }; const result = getAvailableTools([toolWithSpace], ['tool with space']); expect(result).toHaveLength(1); expect(result[0].name).toBe('tool with space'); }); it('should perform case-sensitive filtering', () => { const result = getAvailableTools(testTools, ['AUTH0_LIST_APPLICATIONS']); expect(result).toHaveLength(0); // Should not match due to case difference }); // Since we can't easily mock errors with ESM, we'll test this indirectly it('catches errors during tool pattern matching', () => { // We need to verify the try/catch block in the code works. // Since we can't easily induce an error in a test environment with ESM modules, // we'll verify that the function doesn't throw even with invalid globs. const result = getAvailableTools(testTools, ['[']); // Invalid regex pattern expect(result).toBeDefined(); }); it('should filter tools when readOnly is true', () => { const result = getAvailableTools(testTools, undefined, true); expect(result).toHaveLength(4); // Should only include list_* and get_* tools expect(result.map((t) => t.name)).toContain('auth0_list_applications'); expect(result.map((t) => t.name)).toContain('auth0_get_application'); expect(result.map((t) => t.name)).toContain('auth0_list_resource_servers'); expect(result.map((t) => t.name)).toContain('auth0_get_resource_server'); // Should not include create or update tools expect(result.map((t) => t.name)).not.toContain('auth0_create_application'); expect(result.map((t) => t.name)).not.toContain('auth0_update_application'); }); it('should apply readOnly filter after pattern filtering', () => { // First filter by applications, then apply readOnly const result = getAvailableTools(testTools, ['auth0_*_application*'], true); expect(result).toHaveLength(2); expect(result.map((t) => t.name)).toContain('auth0_list_applications'); expect(result.map((t) => t.name)).toContain('auth0_get_application'); expect(result.map((t) => t.name)).not.toContain('auth0_create_application'); expect(result.map((t) => t.name)).not.toContain('auth0_update_application'); }); it('should prioritize --read-only flag over --tools when both are specified', () => { // Pattern includes all application tools (including create/update) // But --read-only should take priority and filter out non-read-only tools const result = getAvailableTools(testTools, ['auth0_*_application*'], true); // Should ONLY include read-only tools, even though pattern matched non-read-only tools too expect(result.length).toBeGreaterThan(0); expect(result.every((tool) => tool._meta?.readOnly === true)).toBe(true); expect(result.map((t) => t.name)).toContain('auth0_list_applications'); expect(result.map((t) => t.name)).toContain('auth0_get_application'); expect(result.map((t) => t.name)).not.toContain('auth0_create_application'); expect(result.map((t) => t.name)).not.toContain('auth0_update_application'); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/auth0/auth0-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server