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
JavaScript
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