Skip to main content
Glama
webhooks.ts3.55 kB
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { webhookRequestBodySchema } from "../generated/client/schemas/index.js"; import { withErrorHandling } from "../utils/error-handler.js"; import { createEmptyResponse, createJsonResponse, } from "../utils/response-formatter.js"; type HevyClient = ReturnType< typeof import("../utils/hevyClientKubb.js").createClient >; // Enhanced webhook URL validation const webhookUrlSchema = z .string() .url() .refine( (url) => { try { const parsed = new URL(url); return parsed.protocol === "https:" || parsed.protocol === "http:"; } catch { return false; } }, { message: "Webhook URL must be a valid HTTP or HTTPS URL", }, ) .refine( (url) => { try { const parsed = new URL(url); return ( parsed.hostname !== "localhost" && !parsed.hostname.startsWith("127.") ); } catch { return false; } }, { message: "Webhook URL cannot be localhost or loopback address", }, ); export function registerWebhookTools( server: McpServer, hevyClient: HevyClient | null, ) { // Get webhook subscription server.tool( "get-webhook-subscription", "Get the current webhook subscription for this account. Returns the webhook URL and auth token if a subscription exists.", {}, withErrorHandling(async () => { if (!hevyClient) { throw new Error( "API client not initialized. Please provide HEVY_API_KEY.", ); } const data = await hevyClient.getWebhookSubscription(); if (!data) { return createEmptyResponse( "No webhook subscription found for this account", ); } return createJsonResponse(data); }, "get-webhook-subscription"), ); // Create webhook subscription server.tool( "create-webhook-subscription", "Create a new webhook subscription for this account. The webhook will receive POST requests when workouts are created. Your endpoint must respond with 200 OK within 5 seconds.", { url: webhookUrlSchema.describe( "The webhook URL that will receive POST requests when workouts are created", ), authToken: z .string() .optional() .describe( "Optional auth token that will be sent as Authorization header in webhook requests", ), }, withErrorHandling(async ({ url, authToken }) => { if (!hevyClient) { throw new Error( "API client not initialized. Please provide HEVY_API_KEY.", ); } // Validate the request body using the generated schema const requestBody = webhookRequestBodySchema.parse({ url, authToken, }); const data = await hevyClient.createWebhookSubscription(requestBody); if (!data) { return createEmptyResponse( "Failed to create webhook subscription - please check your URL and try again", ); } return createJsonResponse(data); }, "create-webhook-subscription"), ); // Delete webhook subscription server.tool( "delete-webhook-subscription", "Delete the current webhook subscription for this account. This will stop all webhook notifications.", {}, withErrorHandling(async () => { if (!hevyClient) { throw new Error( "API client not initialized. Please provide HEVY_API_KEY.", ); } const data = await hevyClient.deleteWebhookSubscription(); if (!data) { return createEmptyResponse( "Failed to delete webhook subscription - no subscription may exist or there was a server error", ); } return createJsonResponse(data); }, "delete-webhook-subscription"), ); }

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