Skip to main content
Glama
tools.ts6.75 kB
import { Tool } from './types.js'; import { log } from './logger.js'; import { Glob } from './glob.js'; /** * Filters the provided tools collection based on specified glob patterns and readOnly flag. * This function processes the input patterns against available tools to determine * which tools should be returned. It handles special cases like wildcard patterns, * empty pattern arrays, and pattern matching errors. When readOnly is true, * it only returns tools that have _meta.readOnly set to true or tools that follow read-only patterns. * * IMPORTANT: The readOnly flag takes priority over pattern matching for security reasons. * Even if patterns match non-read-only tools, when readOnly=true is specified, * only read-only tools will be returned. * * @param allTools - Complete collection of available tools to be filtered * @param patterns - Optional glob patterns to filter tools by (e.g., 'auth0*', 'jwt-*') * If omitted or empty, all tools will be returned * A single '*' pattern will return all tools * @param readOnly - Optional flag to only return read-only tools * When true, only returns tools marked as readOnly * Takes priority over pattern matching for security * @returns Array of Tool objects that match the specified criteria * Returns all tools if no patterns provided or on error * * @example * // Return all tools that start with "auth" * const authTools = getAvailableTools(tools, ['auth*']); * * @example * // Return all read-only tools (regardless of pattern matching) * const readOnlyTools = getAvailableTools(tools, ['*'], true); * * @example * // Return only read-only tools that match the pattern * // Note: --read-only takes priority, so even if the pattern matches non-read-only tools, * // only the read-only ones will be returned * const readOnlyAuthTools = getAvailableTools(tools, ['auth0_*_application'], true); */ export function getAvailableTools( allTools: Tool[], patterns?: string[], readOnly?: boolean ): Tool[] { // Start with all tools let filteredTools = allTools; // Apply pattern filtering if patterns are provided if (patterns?.length) { filteredTools = filterToolsByPatterns(filteredTools, patterns); } // Apply read-only filtering if requested // IMPORTANT: This is applied AFTER pattern filtering, ensuring that // --read-only takes priority over --tools for security // Even if non-read-only tools match the pattern, they will be filtered out here if (readOnly) { filteredTools = filterToolsByReadOnly(filteredTools); } return filteredTools; } function filterToolsByPatterns(tools: Tool[], patterns: string[]): Tool[] { try { // Special case for global wildcard if (patterns.length === 1 && patterns[0] === '*') { return tools; // Keep all tools, no pattern filtering needed } // Compile glob patterns once for performance const globs = patterns.map((pattern) => new Glob(pattern)); // Track matching tools and matches per pattern const enabledToolNames = new Set<string>(); const matchesByPattern = new Map<string, number>(); // For each tool, check if it matches any pattern for (const tool of tools) { for (const glob of globs) { if (glob.matches(tool.name)) { enabledToolNames.add(tool.name); // Count matches per pattern for logging const patternString = glob.toString(); matchesByPattern.set(patternString, (matchesByPattern.get(patternString) || 0) + 1); // Once we find a match, no need to check other patterns break; } } } // Log match counts for wildcard patterns for debugging for (const [pattern, count] of matchesByPattern.entries()) { if (pattern.includes('*')) { log(`Glob pattern '${pattern}' matched ${count} tools`); } } // Create the filtered tool list based on patterns const filteredTools = tools.filter((tool) => enabledToolNames.has(tool.name)); log(`Selected ${filteredTools.length} available tools based on patterns`); return filteredTools; } catch (error) { // Log error and use all tools as fallback log( `Error determining available tools: ${error instanceof Error ? error.message : String(error)}` ); return tools; } } function filterToolsByReadOnly(tools: Tool[]): Tool[] { const readOnlyTools = tools.filter((tool) => tool._meta?.readOnly === true); log(`Filtered to ${readOnlyTools.length} read-only tools`); return readOnlyTools; } /** * Validates tool patterns against available tools to ensure each pattern matches at least one tool. * This function verifies that each provided pattern (including glob patterns) corresponds to * at least one available tool, throwing specific errors for different validation scenarios. * * @param patterns - Array of tool name patterns to validate * Can include glob patterns with wildcards (e.g., 'auth0*') * Empty array or undefined will skip validation * @param availableTools - Collection of Tool objects to validate patterns against * * @throws {Error} If availableTools is not a valid array or is empty * @throws {Error} If any pattern doesn't match at least one tool name, with different * error messages for exact matches vs. wildcard patterns * * @example * // Validate specific tool names * validatePatterns(['auth0-jwt', 'auth0-management'], tools); * * @example * // Validate with glob patterns * validatePatterns(['auth0*', 'jwt-*'], tools); * * @see {@link Glob} for the pattern matching implementation * @see {@link getAvailableTools} for filtering tools using these patterns */ export function validatePatterns(patterns: string[], availableTools: Tool[]): void { // Skip validation if patterns array is empty if (!patterns || patterns.length === 0) { return; } // Input validation if (!availableTools || !Array.isArray(availableTools)) { throw new Error('Invalid tools array provided for validation'); } if (availableTools.length === 0) { throw new Error('No tools available for pattern validation'); } // Extract tool names for faster matching const toolNames = availableTools.map((tool) => tool.name); // Validate each pattern for (const pattern of patterns) { const glob = new Glob(pattern); const matchesAnyTool = toolNames.some((name) => glob.matches(name)); if (!matchesAnyTool) { const errorPrefix = pattern.includes('*') ? `No tools match the pattern` : `Invalid tool`; throw new Error(`${errorPrefix}: ${pattern}. Accepted tools are: ${toolNames.join(', ')}`); } } }

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