Skip to main content
Glama
templates.test.ts5.79 kB
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { describe, expect, it, vi } from "vitest"; import type { ExerciseTemplate } from "../generated/client/types/index.js"; import { formatExerciseTemplate } from "../utils/formatters.js"; import { registerTemplateTools } from "./templates.js"; type HevyClient = ReturnType< typeof import("../utils/hevyClientKubb.js").createClient >; function createMockServer() { const tool = vi.fn(); const server = { tool } as unknown as McpServer; return { server, tool }; } function getToolRegistration(toolSpy: ReturnType<typeof vi.fn>, name: string) { const match = toolSpy.mock.calls.find(([toolName]) => toolName === name); if (!match) { throw new Error(`Tool ${name} was not registered`); } const [, , , handler] = match as [ string, string, Record<string, unknown>, (args: Record<string, unknown>) => Promise<{ content: Array<{ type: string; text: string }>; isError?: boolean; }>, ]; return { handler }; } describe("registerTemplateTools", () => { it("returns error responses when Hevy client is not initialized", async () => { const { server, tool } = createMockServer(); registerTemplateTools(server, null); const toolNames = [ "get-exercise-templates", "get-exercise-template", "get-exercise-history", "create-exercise-template", ]; for (const name of toolNames) { const { handler } = getToolRegistration(tool, name); const response = await handler({}); expect(response).toMatchObject({ isError: true, content: [ { type: "text", text: expect.stringContaining( "API client not initialized. Please provide HEVY_API_KEY.", ), }, ], }); } }); it("get-exercise-templates returns formatted templates from the client", async () => { const { server, tool } = createMockServer(); const template: ExerciseTemplate = { id: "t1", title: "Bench Press", type: "barbell", primary_muscle_group: "chest", secondary_muscle_groups: ["triceps"], is_custom: false, }; const hevyClient: HevyClient = { getExerciseTemplates: vi .fn() .mockResolvedValue({ exercise_templates: [template] }), } as unknown as HevyClient; registerTemplateTools(server, hevyClient); const { handler } = getToolRegistration(tool, "get-exercise-templates"); const response = await handler({ page: 1, pageSize: 5 }); expect(hevyClient.getExerciseTemplates).toHaveBeenCalledWith({ page: 1, pageSize: 5, }); const parsed = JSON.parse(response.content[0].text) as unknown[]; expect(parsed).toEqual([formatExerciseTemplate(template)]); }); it("get-exercise-template returns an empty response when template is not found", async () => { const { server, tool } = createMockServer(); const hevyClient: HevyClient = { getExerciseTemplate: vi.fn().mockResolvedValue(null), } as unknown as HevyClient; registerTemplateTools(server, hevyClient); const { handler } = getToolRegistration(tool, "get-exercise-template"); const response = await handler({ exerciseTemplateId: "missing-id" }); expect(hevyClient.getExerciseTemplate).toHaveBeenCalledWith("missing-id"); expect(response.content[0]?.text).toBe( "Exercise template with ID missing-id not found", ); }); it("get-exercise-history returns formatted entries", async () => { const { server, tool } = createMockServer(); const hevyClient: HevyClient = { getExerciseHistory: vi.fn().mockResolvedValue({ exercise_history: [ { workout_id: "w1", workout_title: "Push Day", workout_start_time: "2024-01-01T10:00:00Z", workout_end_time: "2024-01-01T11:00:00Z", exercise_template_id: "t1", weight_kg: 80, reps: 8, distance_meters: null, duration_seconds: null, rpe: 8, custom_metric: null, set_type: "normal", }, ], }), } as unknown as HevyClient; registerTemplateTools(server, hevyClient); const { handler } = getToolRegistration(tool, "get-exercise-history"); const response = await handler({ exerciseTemplateId: "t1", startDate: "2024-01-01T00:00:00Z", endDate: "2024-02-01T00:00:00Z", }); expect(hevyClient.getExerciseHistory).toHaveBeenCalledWith("t1", { start_date: "2024-01-01T00:00:00Z", end_date: "2024-02-01T00:00:00Z", }); const parsed = JSON.parse(response.content[0].text) as unknown[]; expect(parsed).toEqual([ { workoutId: "w1", workoutTitle: "Push Day", workoutStartTime: "2024-01-01T10:00:00Z", workoutEndTime: "2024-01-01T11:00:00Z", exerciseTemplateId: "t1", weight: 80, reps: 8, distance: null, duration: null, rpe: 8, customMetric: null, setType: "normal", }, ]); }); it("create-exercise-template maps input to API payload", async () => { const { server, tool } = createMockServer(); const hevyClient: HevyClient = { createExerciseTemplate: vi.fn().mockResolvedValue({ id: 42 }), } as unknown as HevyClient; registerTemplateTools(server, hevyClient); const { handler } = getToolRegistration(tool, "create-exercise-template"); const response = await handler({ title: "Custom Curl", exerciseType: "weight_reps", equipmentCategory: "dumbbell", muscleGroup: "biceps", otherMuscles: ["forearms"], }); expect(hevyClient.createExerciseTemplate).toHaveBeenCalledWith({ exercise: { title: "Custom Curl", exercise_type: "weight_reps", equipment_category: "dumbbell", muscle_group: "biceps", other_muscles: ["forearms"], }, }); const parsed = JSON.parse(response.content[0].text) as { id: number | undefined; message: string; }; expect(parsed).toEqual({ id: 42, message: "Exercise template created successfully", }); }); });

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