UNPKG

okta-mcp-server

Version:

Model Context Protocol (MCP) server for Okta API operations with support for bulk operations and caching

433 lines 13.5 kB
import { z } from 'zod'; import { logger } from '../../utils/logger.js'; import { ListGroupsSchema, GetGroupSchema, CreateGroupSchema, UpdateGroupSchema, DeleteGroupSchema, ListGroupMembersSchema, AddGroupMemberSchema, RemoveGroupMemberSchema, GroupResponseSchema, UserResponseSchema, } from './schemas.js'; /** * Extract pagination cursor from Okta Link header */ function extractCursor(linkHeader) { if (!linkHeader) return undefined; const match = linkHeader.match(/<[^>]+after=([^&>]+)[^>]*>;\s*rel="next"/); return match ? match[1] : undefined; } /** * List Okta groups with pagination and filtering */ export async function handleListGroups(args, client) { try { const params = ListGroupsSchema.parse(args); // Make API call const requestParams = { limit: params.limit, sortOrder: params.sortOrder, }; // Only add optional params if they have values if (params.after) requestParams.after = params.after; if (params.filter) requestParams.filter = params.filter; if (params.search) requestParams.search = params.search; if (params.type) requestParams.type = params.type; if (params.sortBy) requestParams.sortBy = params.sortBy; const response = await client.listGroups(requestParams); logger.debug('Raw Okta response:', { dataLength: response.data.length, headers: response.headers, firstGroup: response.data[0], }); // Extract next cursor from response headers const nextCursor = extractCursor(response.headers?.link); // Format response - with better error handling const groups = []; for (const group of response.data) { try { const validatedGroup = GroupResponseSchema.parse(group); groups.push(validatedGroup); } catch (parseError) { logger.error('Failed to parse group:', { group, error: parseError }); // Skip this group but continue processing others } } const result = { groups, pagination: { limit: params.limit, hasMore: !!nextCursor, nextCursor, }, }; logger.debug('Final result:', { groupCount: groups.length, hasMore: !!nextCursor }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error listing groups:', error); return { content: [ { type: 'text', text: `Error listing groups: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Get a single group by ID */ export async function handleGetGroup(args, client) { try { const { groupId } = GetGroupSchema.parse(args); const group = await client.getGroup(groupId); const validatedGroup = GroupResponseSchema.parse(group); return { content: [ { type: 'text', text: JSON.stringify(validatedGroup, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error getting group:', error); return { content: [ { type: 'text', text: `Error getting group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Create a new group */ export async function handleCreateGroup(args, client) { try { const groupData = CreateGroupSchema.parse(args); const newGroup = await client.createGroup(groupData); const validatedGroup = GroupResponseSchema.parse(newGroup); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Group created successfully', group: validatedGroup, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error creating group:', error); return { content: [ { type: 'text', text: `Error creating group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Update an existing group */ export async function handleUpdateGroup(args, client) { try { const params = UpdateGroupSchema.parse(args); const updatedGroup = await client.updateGroup(params.groupId, { profile: params.profile, }); const validatedGroup = GroupResponseSchema.parse(updatedGroup); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Group updated successfully', group: validatedGroup, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error updating group:', error); return { content: [ { type: 'text', text: `Error updating group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Delete a group */ export async function handleDeleteGroup(args, client) { try { const { groupId } = DeleteGroupSchema.parse(args); await client.deleteGroup(groupId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Group deleted successfully', groupId, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error deleting group:', error); return { content: [ { type: 'text', text: `Error deleting group: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * List members of a group */ export async function handleListGroupMembers(args, client) { try { const params = ListGroupMembersSchema.parse(args); const requestParams = { limit: params.limit, }; if (params.after) requestParams.after = params.after; const response = await client.listGroupMembers(params.groupId, requestParams); logger.debug('Raw Okta response:', { dataLength: response.data.length, headers: response.headers, firstMember: response.data[0], }); // Extract next cursor from response headers const nextCursor = extractCursor(response.headers?.link); // Format response - with better error handling const members = []; for (const member of response.data) { try { const validatedMember = UserResponseSchema.parse(member); members.push(validatedMember); } catch (parseError) { logger.error('Failed to parse member:', { member, error: parseError }); // Skip this member but continue processing others } } const result = { groupId: params.groupId, members, pagination: { limit: params.limit, hasMore: !!nextCursor, nextCursor, }, }; logger.debug('Final result:', { memberCount: members.length, hasMore: !!nextCursor }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error listing group members:', error); return { content: [ { type: 'text', text: `Error listing group members: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Add a user to a group */ export async function handleAddGroupMember(args, client) { try { const { groupId, userId } = AddGroupMemberSchema.parse(args); await client.addGroupMember(groupId, userId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'User added to group successfully', groupId, userId, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error adding group member:', error); return { content: [ { type: 'text', text: `Error adding group member: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Remove a user from a group */ export async function handleRemoveGroupMember(args, client) { try { const { groupId, userId } = RemoveGroupMemberSchema.parse(args); await client.removeGroupMember(groupId, userId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'User removed from group successfully', groupId, userId, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } logger.error('Error removing group member:', error); return { content: [ { type: 'text', text: `Error removing group member: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } //# sourceMappingURL=handlers.js.map