Skip to main content
Glama

Okta MCP Server

import { z } from "zod"; import pkg from "@okta/okta-sdk-nodejs"; const { Client: OktaClient } = pkg; // Schemas for input validation const groupSchemas = { listGroups: z.object({ limit: z.number().min(1).max(200).optional().default(50), filter: z.string().optional(), search: z.string().optional(), after: z.string().optional(), sortBy: z.string().optional(), sortOrder: z.enum(["asc", "desc"]).optional().default("asc"), }), createGroup: z.object({ name: z.string().min(1, "Group name is required"), description: z.string().optional(), }), getGroup: z.object({ groupId: z.string().min(1, "Group ID is required"), }), deleteGroup: z.object({ groupId: z.string().min(1, "Group ID is required"), }), assignUserToGroup: z.object({ groupId: z.string().min(1, "Group ID is required"), userId: z.string().min(1, "User ID is required"), }), removeUserFromGroup: z.object({ groupId: z.string().min(1, "Group ID is required"), userId: z.string().min(1, "User ID is required"), }), listGroupUsers: z.object({ groupId: z.string().min(1, "Group ID is required"), limit: z.number().min(1).max(200).optional().default(50), after: z.string().optional(), }), }; // Utility function to get Okta client (can be moved to a shared utility file) function getOktaClient() { const oktaDomain = process.env.OKTA_ORG_URL; const apiToken = process.env.OKTA_API_TOKEN; if (!oktaDomain) { throw new Error( "OKTA_ORG_URL environment variable is not set. Please set it to your Okta domain." ); } if (!apiToken) { throw new Error( "OKTA_API_TOKEN environment variable is not set. Please generate an API token in the Okta Admin Console." ); } return new OktaClient({ orgUrl: oktaDomain, token: apiToken, }); } // Utility function to format array values function formatArray(arr: string[] | undefined | null): string { if (!arr || arr.length === 0) return "N/A"; return arr.join(", "); } // Utility function to format dates function formatDate(dateString: Date | string | undefined | null): string { if (!dateString) return "N/A"; try { return new Date(dateString).toLocaleString(); } catch (e) { return dateString instanceof Date ? dateString.toISOString() : dateString || "N/A"; } } // Utility function to format profile values function getProfileValue(value: string | undefined | null): string { return value ?? "N/A"; } // Tool definitions for groups export const groupTools = [ { name: "list_groups", description: "List user groups from Okta with optional filtering and pagination", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Maximum number of groups to return (default: 50, max: 200)", }, filter: { type: "string", description: "Filter expression for groups", }, search: { type: "string", description: "Free-form text search across group fields", }, after: { type: "string", description: "Cursor for pagination, obtained from previous response", }, sortBy: { type: "string", description: "Field to sort results by", }, sortOrder: { type: "string", description: "Sort order (asc or desc, default: asc)", enum: ["asc", "desc"], }, }, }, }, { name: "create_group", description: "Create a new group in Okta", inputSchema: { type: "object", properties: { name: { type: "string", description: "Name of the group", }, description: { type: "string", description: "Description of the group (optional)", }, }, required: ["name"], }, }, { name: "get_group", description: "Get detailed information about a specific group", inputSchema: { type: "object", properties: { groupId: { type: "string", description: "ID of the group to retrieve", }, }, required: ["groupId"], }, }, { name: "delete_group", description: "Delete a group from Okta", inputSchema: { type: "object", properties: { groupId: { type: "string", description: "ID of the group to delete", }, }, required: ["groupId"], }, }, { name: "assign_user_to_group", description: "Assign a user to a group in Okta", inputSchema: { type: "object", properties: { groupId: { type: "string", description: "ID of the group", }, userId: { type: "string", description: "ID of the user to assign to the group", }, }, required: ["groupId", "userId"], }, }, { name: "remove_user_from_group", description: "Remove a user from a group in Okta", inputSchema: { type: "object", properties: { groupId: { type: "string", description: "ID of the group", }, userId: { type: "string", description: "ID of the user to remove from the group", }, }, required: ["groupId", "userId"], }, }, { name: "list_group_users", description: "List all users in a specific group", inputSchema: { type: "object", properties: { groupId: { type: "string", description: "ID of the group", }, limit: { type: "number", description: "Maximum number of users to return (default: 50, max: 200)", }, after: { type: "string", description: "Cursor for pagination, obtained from previous response", }, }, required: ["groupId"], }, }, ]; // Handlers for group-related tools export const groupHandlers = { list_groups: async (request: { parameters: unknown }) => { const params = groupSchemas.listGroups.parse(request.parameters); try { // Build query parameters const queryParams: Record<string, any> = {}; if (params.limit) queryParams.limit = params.limit; if (params.after) queryParams.after = params.after; if (params.filter) queryParams.filter = params.filter; if (params.search) queryParams.search = params.search; if (params.sortBy) queryParams.sortBy = params.sortBy; if (params.sortOrder) queryParams.sortOrder = params.sortOrder; const oktaClient = getOktaClient(); // Get groups list const groups = await oktaClient.groupApi.listGroups(queryParams); if (!groups) { return { content: [ { type: "text", text: "No groups data was returned from Okta.", }, ], }; } // Format the response let formattedResponse = "Groups:\n"; let count = 0; // Track pagination info let after: string | undefined; // Process the groups collection for await (const group of groups) { // Check if group is valid if (!group || !group.id) { continue; } count++; // Remember the last group ID for pagination after = group.id; formattedResponse += ` ${count}. ${group.profile?.name || "Unnamed Group"} - ID: ${group.id} - Type: ${group.type || "Unknown"} - Object Class: ${formatArray(group.objectClass)} - Description: ${group.profile?.description || "No description"} - Created: ${formatDate(group.created)} - Last Updated: ${formatDate(group.lastUpdated)} - Last Membership Updated: ${formatDate(group.lastMembershipUpdated)} `; } if (count === 0) { return { content: [ { type: "text", text: "No groups found matching your criteria.", }, ], }; } // Add pagination information if (after && count >= (params.limit || 50)) { formattedResponse += `\nPagination:\n- Total groups shown: ${count}\n`; formattedResponse += `- For next page, use 'after' parameter with value: ${after}\n`; } else { formattedResponse += `\nTotal groups: ${count}\n`; } return { content: [ { type: "text", text: formattedResponse, }, ], }; } catch (error) { console.error("Error listing groups:", error); return { content: [ { type: "text", text: `Failed to list groups: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, create_group: async (request: { parameters: unknown }) => { const { name, description } = groupSchemas.createGroup.parse( request.parameters ); try { const oktaClient = getOktaClient(); const newGroup = { profile: { name, description: description || "", }, }; const group = await oktaClient.groupApi.createGroup({ group: newGroup, }); return { content: [ { type: "text", text: `Group created successfully: ID: ${group.id} Name: ${group.profile?.name} Type: ${group.type || "OKTA_GROUP"} Created: ${formatDate(group.created)}`, }, ], }; } catch (error) { console.error("Error creating group:", error); return { content: [ { type: "text", text: `Failed to create group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, get_group: async (request: { parameters: unknown }) => { const { groupId } = groupSchemas.getGroup.parse(request.parameters); try { const oktaClient = getOktaClient(); const group = await oktaClient.groupApi.getGroup({ groupId, }); if (!group || !group.profile) { return { content: [ { type: "text", text: `No group found with ID: ${groupId}`, }, ], }; } const formattedGroup = `Group Details: - ID: ${group.id} - Name: ${group.profile.name} - Description: ${getProfileValue(group.profile.description)} - Type: ${group.type || "Unknown"} - Object Class: ${formatArray(group.objectClass)} - Created: ${formatDate(group.created)} - Last Updated: ${formatDate(group.lastUpdated)} - Last Membership Updated: ${formatDate(group.lastMembershipUpdated)}`; return { content: [ { type: "text", text: formattedGroup, }, ], }; } catch (error) { console.error("Error getting group:", error); return { content: [ { type: "text", text: `Failed to get group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, delete_group: async (request: { parameters: unknown }) => { const { groupId } = groupSchemas.deleteGroup.parse(request.parameters); try { const oktaClient = getOktaClient(); await oktaClient.groupApi.deleteGroup({ groupId, }); return { content: [ { type: "text", text: `Group with ID ${groupId} has been successfully deleted.`, }, ], }; } catch (error) { console.error("Error deleting group:", error); return { content: [ { type: "text", text: `Failed to delete group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, assign_user_to_group: async (request: { parameters: unknown }) => { const { groupId, userId } = groupSchemas.assignUserToGroup.parse( request.parameters ); try { const oktaClient = getOktaClient(); await oktaClient.groupApi.assignUserToGroup({ groupId, userId, }); return { content: [ { type: "text", text: `User with ID ${userId} has been successfully assigned to group with ID ${groupId}.`, }, ], }; } catch (error) { console.error("Error assigning user to group:", error); return { content: [ { type: "text", text: `Failed to assign user to group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, remove_user_from_group: async (request: { parameters: unknown }) => { const { groupId, userId } = groupSchemas.removeUserFromGroup.parse( request.parameters ); try { const oktaClient = getOktaClient(); await oktaClient.groupApi.unassignUserFromGroup({ groupId, userId, }); return { content: [ { type: "text", text: `User with ID ${userId} has been successfully removed from group with ID ${groupId}.`, }, ], }; } catch (error) { console.error("Error removing user from group:", error); return { content: [ { type: "text", text: `Failed to remove user from group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, list_group_users: async (request: { parameters: unknown }) => { const params = groupSchemas.listGroupUsers.parse(request.parameters); try { const oktaClient = getOktaClient(); // Build query parameters for pagination const queryParams: Record<string, any> = {}; if (params.limit) queryParams.limit = params.limit; if (params.after) queryParams.after = params.after; // Get group users list const users = await oktaClient.groupApi.listGroupUsers({ groupId: params.groupId, ...queryParams, }); if (!users) { return { content: [ { type: "text", text: "No users data was returned from Okta.", }, ], }; } // Format the response let formattedResponse = `Users in Group (ID: ${params.groupId}):\n`; let count = 0; // Track pagination info let after: string | undefined; // Process the users collection for await (const user of users) { // Check if user is valid if (!user || !user.id) { continue; } count++; // Remember the last user ID for pagination after = user.id; formattedResponse += ` ${count}. ${user.profile?.firstName || ""} ${user.profile?.lastName || ""} (${user.profile?.email || "No email"}) - ID: ${user.id} - Status: ${user.status || "Unknown"} - Created: ${formatDate(user.created)} - Last Updated: ${formatDate(user.lastUpdated)} `; } if (count === 0) { return { content: [ { type: "text", text: "No users found in this group.", }, ], }; } // Add pagination information if (after && count >= (params.limit || 50)) { formattedResponse += `\nPagination:\n- Total users shown: ${count}\n`; formattedResponse += `- For next page, use 'after' parameter with value: ${after}\n`; } else { formattedResponse += `\nTotal users in group: ${count}\n`; } return { content: [ { type: "text", text: formattedResponse, }, ], }; } catch (error) { console.error("Error listing group users:", error); return { content: [ { type: "text", text: `Failed to list group users: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, };

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/kapilduraphe/okta-mcp-server'

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