Skip to main content
Glama

manage_graph_subscriptions

Create, update, delete, or list webhook subscriptions to receive real-time change notifications from Microsoft Graph resources.

Instructions

Manage webhook subscriptions for real-time change notifications from Microsoft Graph resources.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesSubscription management action
subscriptionIdNoSubscription ID for update/delete operations
subscriptionNoSubscription details for create/update
updatesNoUpdates for existing subscription

Implementation Reference

  • Registration and inline handler implementation for manage_graph_subscriptions tool. Dispatches actions (create, update, delete, list) to GraphAdvancedFeatures class methods.
    // Webhook Subscriptions const graphSubscriptionsMeta = getToolMetadata("manage_graph_subscriptions")!; this.server.tool( "manage_graph_subscriptions", graphSubscriptionsMeta.description, z.object({ action: z.enum(['create', 'update', 'delete', 'list']).describe('Subscription management action'), subscriptionId: z.string().optional().describe('Subscription ID for update/delete operations'), subscription: webhookSubscriptionSchema.optional().describe('Subscription details for create/update'), updates: webhookSubscriptionSchema.partial().optional().describe('Updates for existing subscription') }).shape, graphSubscriptionsMeta.annotations || {}, wrapToolHandler(async (args: any) => { this.validateCredentials(); try { const advancedFeatures = new GraphAdvancedFeatures(this.getGraphClient(), this.getAccessToken.bind(this)); let result: any; switch (args.action) { case 'create': if (!args.subscription) { throw new McpError(ErrorCode.InvalidParams, 'Subscription details required for create action'); } result = await advancedFeatures.createSubscription(args.subscription); break; case 'update': if (!args.subscriptionId || !args.updates) { throw new McpError(ErrorCode.InvalidParams, 'Subscription ID and updates required for update action'); } result = await advancedFeatures.updateSubscription(args.subscriptionId, args.updates); break; case 'delete': if (!args.subscriptionId) { throw new McpError(ErrorCode.InvalidParams, 'Subscription ID required for delete action'); } result = await advancedFeatures.deleteSubscription(args.subscriptionId); break; case 'list': result = await advancedFeatures.listSubscriptions(); break; default: throw new McpError(ErrorCode.InvalidParams, `Invalid action: ${args.action}`); } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Error managing subscriptions: ${error instanceof Error ? error.message : 'Unknown error'}` ); } }) );
  • GraphAdvancedFeatures class providing core helper methods for managing Graph subscriptions: createSubscription, updateSubscription, deleteSubscription, listSubscriptions.
    async createSubscription(subscription: WebhookSubscription): Promise<SubscriptionResponse> { const subscriptionPayload: any = { changeType: subscription.changeTypes.join(','), notificationUrl: subscription.notificationUrl, resource: subscription.resource, expirationDateTime: subscription.expirationDateTime || this.getDefaultExpiration(), clientState: subscription.clientState, latestSupportedTlsVersion: subscription.tlsVersion || 'v1_2' }; // Add lifecycle notification URL if provided if (subscription.lifecycleNotificationUrl) { subscriptionPayload.lifecycleNotificationUrl = subscription.lifecycleNotificationUrl; } try { const response = await this.graphClient .api('/subscriptions') .post(subscriptionPayload); return { id: response.id, resource: response.resource, changeType: response.changeType, notificationUrl: response.notificationUrl, expirationDateTime: response.expirationDateTime, clientState: response.clientState, createdAt: new Date().toISOString() }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to create subscription: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // Update existing subscription async updateSubscription(subscriptionId: string, updates: Partial<WebhookSubscription>): Promise<SubscriptionResponse> { const updatePayload: any = {}; if (updates.expirationDateTime) { updatePayload.expirationDateTime = updates.expirationDateTime; } if (updates.notificationUrl) { updatePayload.notificationUrl = updates.notificationUrl; } try { const response = await this.graphClient .api(`/subscriptions/${subscriptionId}`) .patch(updatePayload); return { id: response.id, resource: response.resource, changeType: response.changeType, notificationUrl: response.notificationUrl, expirationDateTime: response.expirationDateTime, clientState: response.clientState, updatedAt: new Date().toISOString() }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to update subscription: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // Delete subscription async deleteSubscription(subscriptionId: string): Promise<{ deleted: boolean; deletedAt: string }> { try { await this.graphClient .api(`/subscriptions/${subscriptionId}`) .delete(); return { deleted: true, deletedAt: new Date().toISOString() }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to delete subscription: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // List all subscriptions async listSubscriptions(): Promise<SubscriptionResponse[]> { try { const response = await this.graphClient .api('/subscriptions') .get(); return response.value.map((sub: any) => ({ id: sub.id, resource: sub.resource, changeType: sub.changeType, notificationUrl: sub.notificationUrl, expirationDateTime: sub.expirationDateTime, clientState: sub.clientState })); } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to list subscriptions: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // Advanced Search - Use Microsoft Search API async executeSearch(query: SearchQuery): Promise<SearchResponse> { const searchPayload = { requests: [{ entityTypes: query.entityTypes, query: { queryString: query.queryString }, from: query.from || 0, size: query.size || 25, fields: query.fields, sortProperties: query.sortProperties, aggregations: query.aggregations, queryAlterationOptions: query.queryAlterationOptions }] }; try { const response = await this.graphClient .api('/search/query') .post(searchPayload); const searchResults = response.value[0]; return { hits: searchResults.hitsContainers[0]?.hits || [], totalCount: searchResults.hitsContainers[0]?.total || 0, moreResultsAvailable: searchResults.hitsContainers[0]?.moreResultsAvailable || false, aggregations: searchResults.hitsContainers[0]?.aggregations || [], queryAlterationResponse: searchResults.queryAlterationResponse, searchedAt: new Date().toISOString() }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Search query failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // Get default expiration time for subscriptions (maximum allowed) private getDefaultExpiration(): string { const now = new Date(); // Most subscriptions have a maximum lifetime of 4230 minutes (about 3 days) now.setMinutes(now.getMinutes() + 4230); return now.toISOString(); } // Validate webhook notification (for webhook endpoint implementation) validateWebhookNotification(notification: any, clientState?: string): boolean { if (!notification || !notification.value) { return false; } // Validate client state if provided if (clientState && notification.clientState !== clientState) { return false; } // Validate required fields const requiredFields = ['subscriptionId', 'changeType', 'resource']; for (const field of requiredFields) { if (!notification[field]) { return false; } } return true; } // Process webhook notification processWebhookNotification(notification: any): ProcessedNotification { return { subscriptionId: notification.subscriptionId, changeType: notification.changeType, resource: notification.resource, resourceData: notification.resourceData, subscriptionExpirationDateTime: notification.subscriptionExpirationDateTime, clientState: notification.clientState, tenantId: notification.tenantId, processedAt: new Date().toISOString() }; } }
  • Zod schema for webhook subscription input validation, used in tool schema and helpers.
    export const webhookSubscriptionSchema = z.object({ resource: z.string().describe('Graph resource to monitor'), changeTypes: z.array(z.enum(['created', 'updated', 'deleted'])).describe('Types of changes to monitor'), notificationUrl: z.string().url().describe('Webhook endpoint URL'), expirationDateTime: z.string().optional().describe('Subscription expiration (ISO 8601)'), clientState: z.string().optional().describe('Client state for validation'), lifecycleNotificationUrl: z.string().url().optional().describe('Lifecycle notification URL'), tlsVersion: z.enum(['v1_0', 'v1_1', 'v1_2', 'v1_3']).optional().describe('Minimum TLS version')
  • Tool metadata including description, title, and annotations for manage_graph_subscriptions.
    manage_graph_subscriptions: { description: "Manage webhook subscriptions for real-time change notifications from Microsoft Graph resources.", title: "Graph Subscription Manager", annotations: { title: "Graph Subscription Manager", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true } },

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/DynamicEndpoints/m365-core-mcp'

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