manage-webhook
Subscribe, find, or unsubscribe webhooks for the 'orders/updated' topic on Shopify. Manage webhook subscriptions using a callback URL and webhook ID for streamlined integration.
Instructions
Subscribe, find, or unsubscribe webhooks
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform with webhook | |
| callbackUrl | Yes | Webhook callback URL | |
| topic | Yes | Webhook topic to subscribe to | |
| webhookId | No | Webhook ID (required for unsubscribe) |
Implementation Reference
- src/index.ts:574-636 (registration)MCP tool registration for 'manage-webhook', including input schema validation and the main handler logic that dispatches to ShopifyClient methods based on action.server.tool( "manage-webhook", "Subscribe, find, or unsubscribe webhooks", { action: z .enum(["subscribe", "find", "unsubscribe"]) .describe("Action to perform with webhook"), callbackUrl: z.string().url().describe("Webhook callback URL"), topic: z .nativeEnum(ShopifyWebhookTopic) .describe("Webhook topic to subscribe to"), webhookId: z .string() .optional() .describe("Webhook ID (required for unsubscribe)"), }, async ({ action, callbackUrl, topic, webhookId }) => { const client = new ShopifyClient(); try { switch (action) { case "subscribe": { const webhook = await client.subscribeWebhook( SHOPIFY_ACCESS_TOKEN, MYSHOPIFY_DOMAIN, callbackUrl, topic ); return { content: [{ type: "text", text: JSON.stringify(webhook, null, 2) }], }; } case "find": { const webhook = await client.findWebhookByTopicAndCallbackUrl( SHOPIFY_ACCESS_TOKEN, MYSHOPIFY_DOMAIN, callbackUrl, topic ); return { content: [{ type: "text", text: JSON.stringify(webhook, null, 2) }], }; } case "unsubscribe": { if (!webhookId) { throw new Error("webhookId is required for unsubscribe action"); } await client.unsubscribeWebhook( SHOPIFY_ACCESS_TOKEN, MYSHOPIFY_DOMAIN, webhookId ); return { content: [ { type: "text", text: "Webhook unsubscribed successfully" }, ], }; } } } catch (error) { return handleError("Failed to manage webhook", error); } } );
- Helper method to subscribe/create a new webhook subscription via Shopify GraphQL API.async subscribeWebhook( accessToken: string, shop: string, callbackUrl: string, topic: ShopifyWebhookTopic ): Promise<ShopifyWebhook> { const myshopifyDomain = await this.getMyShopifyDomain(accessToken, shop); const graphqlQuery = gql` mutation webhookSubscriptionCreate( $topic: WebhookSubscriptionTopic! $webhookSubscription: WebhookSubscriptionInput! ) { webhookSubscriptionCreate( topic: $topic webhookSubscription: $webhookSubscription ) { webhookSubscription { id topic endpoint { __typename ... on WebhookHttpEndpoint { callbackUrl } } } userErrors { field message } } } `; const res = await this.shopifyGraphqlRequest<{ data: { webhookSubscriptionCreate: { webhookSubscription: { id: string; topic: ShopifyWebhookTopicGraphql; endpoint: { callbackUrl: string; }; }; userErrors: Array<{ field: string[]; message: string; }>; }; }; }>({ url: `https://${myshopifyDomain}/admin/api/${this.SHOPIFY_API_VERSION}/graphql.json`, accessToken, query: graphqlQuery, variables: { topic: this.mapTopicToGraphqlTopic(topic), webhookSubscription: { callbackUrl, }, }, }); const webhookSubscription = res.data.data.webhookSubscriptionCreate.webhookSubscription; const userErrors = res.data.data.webhookSubscriptionCreate.userErrors; if (userErrors.length > 0) { throw getGraphqlShopifyUserError(userErrors, { shop, topic, callbackUrl: callbackUrl, }); } return { id: webhookSubscription.id, topic: this.mapGraphqlTopicToTopic(webhookSubscription.topic), callbackUrl: webhookSubscription.endpoint.callbackUrl, }; }
- Helper method to find existing webhook by topic and callback URL via Shopify GraphQL API.async findWebhookByTopicAndCallbackUrl( accessToken: string, shop: string, callbackUrl: string, topic: ShopifyWebhookTopic ): Promise<ShopifyWebhook | null> { const myshopifyDomain = await this.getMyShopifyDomain(accessToken, shop); const graphqlQuery = gql` query webhookSubscriptions( $topics: [WebhookSubscriptionTopic!] $callbackUrl: URL! ) { webhookSubscriptions( first: 10 topics: $topics callbackUrl: $callbackUrl ) { edges { node { id topic endpoint { __typename ... on WebhookHttpEndpoint { callbackUrl } } } } } } `; const res = await this.shopifyGraphqlRequest<{ data: { webhookSubscriptions: { edges: { node: { id: string; topic: ShopifyWebhookTopicGraphql; endpoint: { callbackUrl: string; }; }; }[]; }; }; }>({ url: `https://${myshopifyDomain}/admin/api/${this.SHOPIFY_API_VERSION}/graphql.json`, accessToken, query: graphqlQuery, variables: { topics: [this.mapTopicToGraphqlTopic(topic)], callbackUrl, }, }); const webhookSubscriptions = res.data.data.webhookSubscriptions.edges; if (webhookSubscriptions.length === 0) { return null; } const webhookSubscription = webhookSubscriptions[0].node; return { id: webhookSubscription.id, topic: this.mapGraphqlTopicToTopic(webhookSubscription.topic), callbackUrl: webhookSubscription.endpoint.callbackUrl, }; }
- Helper method to unsubscribe/delete a webhook by ID via Shopify GraphQL API.async unsubscribeWebhook( accessToken: string, shop: string, webhookId: string ): Promise<void> { const myshopifyDomain = await this.getMyShopifyDomain(accessToken, shop); const graphqlQuery = gql` mutation webhookSubscriptionDelete($id: ID!) { webhookSubscriptionDelete(id: $id) { userErrors { field message } deletedWebhookSubscriptionId } } `; const res = await this.shopifyGraphqlRequest<{ data: { webhookSubscriptionDelete: { deletedWebhookSubscriptionId: string; userErrors: Array<{ field: string[]; message: string; }>; }; }; }>({ url: `https://${myshopifyDomain}/admin/api/${this.SHOPIFY_API_VERSION}/graphql.json`, accessToken, query: graphqlQuery, variables: { id: webhookId, }, }); const userErrors = res.data.data.webhookSubscriptionDelete.userErrors; if (userErrors.length > 0) { throw getGraphqlShopifyUserError(userErrors, { shop, webhookId, }); } }
- Type definitions and enums for webhook topics and ShopifyWebhook used in the tool schema and helpers.export enum ShopifyWebhookTopicGraphql { ORDERS_UPDATED = "ORDERS_UPDATED", } export enum ShopifyWebhookTopic { ORDERS_UPDATED = "orders/updated", } export type ShopifyWebhook = { id: string; callbackUrl: string; topic: ShopifyWebhookTopic; };