Skip to main content
Glama

search-exercise-templates

Search fitness exercise templates by name and filter by primary muscle group. Results are cached for fast repeated access, with option to refresh the cache.

Instructions

Search exercise templates by name with optional muscle group filter. Fetches all templates from the Hevy API on first call and caches them in memory for subsequent searches. Use refresh:true to force a re-fetch.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesCase-insensitive substring to match against exercise template titles
primaryMuscleGroupNoOptional filter to restrict results to a specific primary muscle group
refreshNoSet to true to bust the in-memory cache and re-fetch all templates from the API

Implementation Reference

  • The handler function for the search-exercise-templates tool. It fetches all exercise templates from the Hevy API with pagination (caching them in memory), then filters by case-insensitive query and optional primary muscle group. Supports a refresh flag to bust the cache.
    server.tool(
    	"search-exercise-templates",
    	"Search exercise templates by name with optional muscle group filter. Fetches all templates from the Hevy API on first call and caches them in memory for subsequent searches. Use refresh:true to force a re-fetch.",
    	searchExerciseTemplatesSchema,
    	withErrorHandling(async (args: SearchExerciseTemplatesParams) => {
    		if (!hevyClient) {
    			throw new Error(
    				"API client not initialized. Please provide HEVY_API_KEY.",
    			);
    		}
    		const { query, primaryMuscleGroup, refresh } = args;
    
    		// Populate cache if empty or refresh requested.
    		// Use an in-flight promise to prevent concurrent duplicate fetches.
    		if (exerciseTemplateCache === null || refresh) {
    			if (refresh) exerciseTemplateFetch = null;
    
    			if (exerciseTemplateFetch === null) {
    				exerciseTemplateFetch = (async () => {
    					const allTemplates: ExerciseTemplate[] = [];
    					let page = 1;
    					let pageCount = 1;
    
    					do {
    						const data: GetV1ExerciseTemplates200 =
    							await hevyClient.getExerciseTemplates({
    								page,
    								pageSize: 100,
    							});
    
    						const templates = data?.exercise_templates ?? [];
    						allTemplates.push(...templates);
    						pageCount = data?.page_count ?? 1;
    						page++;
    					} while (page <= pageCount);
    
    					exerciseTemplateCache = allTemplates;
    					exerciseTemplateFetch = null;
    					return allTemplates;
    				})();
    			}
    
    			await exerciseTemplateFetch;
    		}
    
    		// Filter by query (case-insensitive title substring match)
    		const queryLower = query.toLowerCase();
    		if (exerciseTemplateCache === null) {
    			throw new Error("Failed to populate exercise template cache.");
    		}
    		let results = exerciseTemplateCache.filter((t) =>
    			(t.title ?? "").toLowerCase().includes(queryLower),
    		);
    
    		// Optional primary muscle group filter
    		if (primaryMuscleGroup !== undefined) {
    			results = results.filter(
    				(t) => t.primary_muscle_group === primaryMuscleGroup,
    			);
    		}
    
    		if (results.length === 0) {
    			return createEmptyResponse(
    				`No exercise templates found matching "${query}"${primaryMuscleGroup ? ` with primary muscle group "${primaryMuscleGroup}"` : ""}`,
    			);
    		}
    
    		return createJsonResponse(results.map(formatExerciseTemplate));
    	}, "search-exercise-templates"),
  • Zod schema defining the input parameters for search-exercise-templates: query (string, required), primaryMuscleGroup (enum from MUSCLE_GROUPS, optional), and refresh (boolean, default false).
    const searchExerciseTemplatesSchema = {
    	query: z
    		.string()
    		.min(1)
    		.describe(
    			"Case-insensitive substring to match against exercise template titles",
    		),
    	primaryMuscleGroup: z
    		.enum(MUSCLE_GROUPS)
    		.optional()
    		.describe(
    			"Optional filter to restrict results to a specific primary muscle group",
    		),
    	refresh: z
    		.boolean()
    		.optional()
    		.default(false)
    		.describe(
    			"Set to true to bust the in-memory cache and re-fetch all templates from the API",
    		),
    } as const;
    type SearchExerciseTemplatesParams = InferToolParams<
    	typeof searchExerciseTemplatesSchema
    >;
  • src/index.ts:47-77 (registration)
    The tool is registered via registerTemplateTools() in the main server setup, which is called from src/index.ts.
    import { registerTemplateTools } from "./tools/templates.js";
    import { registerWebhookTools } from "./tools/webhooks.js";
    import { registerWorkoutTools } from "./tools/workouts.js";
    import { assertApiKey, parseConfig } from "./utils/config.js";
    import { createClient } from "./utils/hevyClient.js";
    
    const HEVY_API_BASEURL = "https://api.hevyapp.com";
    
    const serverConfigSchema = z.object({
    	apiKey: z
    		.string()
    		.min(1, "Hevy API key is required")
    		.describe("Your Hevy API key (available in the Hevy app settings)."),
    });
    
    export const configSchema = serverConfigSchema;
    type ServerConfig = z.infer<typeof serverConfigSchema>;
    
    function buildServer(apiKey: string) {
    	const baseServer = new McpServer({
    		name,
    		version,
    	});
    	const server = Sentry.wrapMcpServerWithSentry(baseServer);
    
    	const hevyClient = createClient(apiKey, HEVY_API_BASEURL);
    	console.error("Hevy client initialized with API key");
    
    	registerWorkoutTools(server, hevyClient);
    	registerRoutineTools(server, hevyClient);
    	registerTemplateTools(server, hevyClient);
  • Helper function that formats exercise template objects from the API into a standardized shape (id, title, type, primaryMuscleGroup, secondaryMuscleGroups, isCustom) before returning results.
    export function formatExerciseTemplate(
    	template: ExerciseTemplate,
    ): FormattedExerciseTemplate {
    	return {
    		id: template.id,
    		title: template.title,
    		type: template.type,
    		primaryMuscleGroup: template.primary_muscle_group,
    		secondaryMuscleGroups: template.secondary_muscle_groups,
    		isCustom: template.is_custom,
    	};
    }
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but description fully discloses caching mechanism: fetches all on first call, caches in memory, and provides refresh parameter to force re-fetch. Excellent transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with purpose, then caching behavior. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers action, optional filters, caching, and refresh. Slightly lacking output format details, but for a search tool with no output schema, it is fairly complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%; description reinforces schema details for query (case-insensitive) and refresh (cache busting) but does not add significant new meaning beyond the structured descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool searches exercise templates by name with optional muscle group filter, distinguishing it from siblings like get-exercise-templates (all) and get-exercise-template (by ID).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly describes when to use (search by name) and caching behavior with refresh option. Could mention alternatives but not necessary for clarity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/chrisdoc/hevy-mcp'

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