UNPKG

okta-mcp-server

Version:

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

760 lines 23.8 kB
import { z } from 'zod'; import { logger } from '../../utils/logger.js'; import { ListAppsSchema, GetAppSchema, CreateAppSchema, UpdateAppSchema, DeleteAppSchema, AppLifecycleSchema, AssignUserToAppSchema, AssignGroupToAppSchema, RemoveUserFromAppSchema, RemoveGroupFromAppSchema, ListAppUsersSchema, ListAppGroupsSchema, UpdateAppUserSchema, AppResponseSchema, AppUserResponseSchema, AppGroupResponseSchema, } 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 applications with pagination and filtering */ export async function handleListApps(args, client) { try { const params = ListAppsSchema.parse(args); // Make API call const requestParams = { limit: params.limit, }; // Only add optional params if they have values if (params.after) requestParams.after = params.after; if (params.filter) requestParams.filter = params.filter; if (params.q) requestParams.q = params.q; if (params.expand) requestParams.expand = params.expand; const response = await client.listApps(requestParams); // Extract next cursor from response headers const nextCursor = extractCursor(response.headers?.link); // Format response - with better error handling const apps = []; for (const app of response.data) { try { const validatedApp = AppResponseSchema.parse(app); apps.push(validatedApp); } catch (parseError) { logger.error('Failed to parse app:', { app, error: parseError }); // Include the app anyway but log the error apps.push(app); } } const result = { apps, pagination: { limit: params.limit, hasMore: !!nextCursor, 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, }; } return { content: [ { type: 'text', text: `Error listing apps: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Get a single application by ID */ export async function handleGetApp(args, client) { try { const params = GetAppSchema.parse(args); const app = await client.getApp(params.appId, params.expand); const validatedApp = AppResponseSchema.parse(app); return { content: [ { type: 'text', text: JSON.stringify(validatedApp, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error getting app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Create a new application */ export async function handleCreateApp(args, client) { try { const params = CreateAppSchema.parse(args); // Build app data - only include defined properties const appData = { name: params.name, label: params.label, signOnMode: params.signOnMode, }; if (params.status) appData.status = params.status; if (params.accessibility) appData.accessibility = params.accessibility; if (params.visibility) appData.visibility = params.visibility; if (params.features) appData.features = params.features; if (params.settings) appData.settings = params.settings; if (params.credentials) appData.credentials = params.credentials; if (params.licensing) appData.licensing = params.licensing; const app = await client.createApp(appData); const validatedApp = AppResponseSchema.parse(app); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Application created successfully', app: validatedApp, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error creating app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Update an existing application */ export async function handleUpdateApp(args, client) { try { const params = UpdateAppSchema.parse(args); // Build update data - only include defined properties const updateData = {}; if (params.name) updateData.name = params.name; if (params.label) updateData.label = params.label; if (params.status) updateData.status = params.status; if (params.accessibility) updateData.accessibility = params.accessibility; if (params.visibility) updateData.visibility = params.visibility; if (params.features) updateData.features = params.features; if (params.settings) updateData.settings = params.settings; if (params.credentials) updateData.credentials = params.credentials; if (params.licensing) updateData.licensing = params.licensing; const app = await client.updateApp(params.appId, updateData); const validatedApp = AppResponseSchema.parse(app); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Application updated successfully', app: validatedApp, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error updating app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Delete an application */ export async function handleDeleteApp(args, client) { try { const params = DeleteAppSchema.parse(args); // First deactivate if necessary, then delete await client.deleteApp(params.appId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Application deleted successfully', appId: params.appId, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error deleting app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Activate an application */ export async function handleActivateApp(args, client) { try { const params = AppLifecycleSchema.parse(args); await client.activateApp(params.appId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Application activated successfully', appId: params.appId, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error activating app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Deactivate an application */ export async function handleDeactivateApp(args, client) { try { const params = AppLifecycleSchema.parse(args); await client.deactivateApp(params.appId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Application deactivated successfully', appId: params.appId, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error deactivating app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Assign a user to an application */ export async function handleAssignUserToApp(args, client) { try { const params = AssignUserToAppSchema.parse(args); // Build assignment data const assignmentData = {}; if (params.profile) assignmentData.profile = params.profile; if (params.credentials) assignmentData.credentials = params.credentials; const assignment = await client.assignUserToApp(params.appId, params.userId, assignmentData); const validatedAssignment = AppUserResponseSchema.parse(assignment); return { content: [ { type: 'text', text: JSON.stringify({ message: 'User assigned to application successfully', assignment: validatedAssignment, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error assigning user to app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Assign a group to an application */ export async function handleAssignGroupToApp(args, client) { try { const params = AssignGroupToAppSchema.parse(args); // Build assignment data const assignmentData = {}; if (params.priority !== undefined) assignmentData.priority = params.priority; if (params.profile) assignmentData.profile = params.profile; const assignment = await client.assignGroupToApp(params.appId, params.groupId, assignmentData); const validatedAssignment = AppGroupResponseSchema.parse(assignment); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Group assigned to application successfully', assignment: validatedAssignment, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error assigning group to app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Remove a user from an application */ export async function handleRemoveUserFromApp(args, client) { try { const params = RemoveUserFromAppSchema.parse(args); await client.removeUserFromApp(params.appId, params.userId, params.sendEmail); return { content: [ { type: 'text', text: JSON.stringify({ message: 'User removed from application successfully', appId: params.appId, userId: params.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, }; } return { content: [ { type: 'text', text: `Error removing user from app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Remove a group from an application */ export async function handleRemoveGroupFromApp(args, client) { try { const params = RemoveGroupFromAppSchema.parse(args); await client.removeGroupFromApp(params.appId, params.groupId); return { content: [ { type: 'text', text: JSON.stringify({ message: 'Group removed from application successfully', appId: params.appId, groupId: params.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, }; } return { content: [ { type: 'text', text: `Error removing group from app: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * List users assigned to an application */ export async function handleListAppUsers(args, client) { try { const params = ListAppUsersSchema.parse(args); // Make API call const requestParams = { limit: params.limit, }; // Only add optional params if they have values if (params.after) requestParams.after = params.after; if (params.q) requestParams.q = params.q; if (params.filter) requestParams.filter = params.filter; if (params.expand) requestParams.expand = params.expand; const response = await client.listAppUsers(params.appId, requestParams); // Extract next cursor from response headers const nextCursor = extractCursor(response.headers?.link); // Format response const users = []; for (const user of response.data) { try { const validatedUser = AppUserResponseSchema.parse(user); users.push(validatedUser); } catch (parseError) { logger.error('Failed to parse app user:', { user, error: parseError }); users.push(user); } } const result = { users, pagination: { limit: params.limit, hasMore: !!nextCursor, 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, }; } return { content: [ { type: 'text', text: `Error listing app users: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * List groups assigned to an application */ export async function handleListAppGroups(args, client) { try { const params = ListAppGroupsSchema.parse(args); // Make API call const requestParams = { limit: params.limit, }; // Only add optional params if they have values if (params.after) requestParams.after = params.after; if (params.q) requestParams.q = params.q; if (params.expand) requestParams.expand = params.expand; const response = await client.listAppGroups(params.appId, requestParams); // Extract next cursor from response headers const nextCursor = extractCursor(response.headers?.link); // Format response const groups = []; for (const group of response.data) { try { const validatedGroup = AppGroupResponseSchema.parse(group); groups.push(validatedGroup); } catch (parseError) { logger.error('Failed to parse app group:', { group, error: parseError }); groups.push(group); } } const result = { groups, pagination: { limit: params.limit, hasMore: !!nextCursor, 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, }; } return { content: [ { type: 'text', text: `Error listing app groups: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Update a user's app-specific profile or credentials */ export async function handleUpdateAppUser(args, client) { try { const params = UpdateAppUserSchema.parse(args); // Build update data - only include defined properties const updateData = {}; if (params.profile) updateData.profile = params.profile; if (params.credentials) updateData.credentials = params.credentials; const user = await client.updateAppUser(params.appId, params.userId, updateData); const validatedUser = AppUserResponseSchema.parse(user); return { content: [ { type: 'text', text: JSON.stringify({ message: 'App user updated successfully', user: validatedUser, }, null, 2), }, ], }; } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Validation error: ${JSON.stringify(error.errors, null, 2)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error updating app user: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } //# sourceMappingURL=handlers.js.map