@indranilokg/okta-mcp-server
Version:
Okta MCP (Model Context Protocol) server for managing Okta applications, users, and groups
322 lines (296 loc) • 11.1 kB
JavaScript
/**
* Function to create a new user in an Okta organization.
*
* @param {Object} args - Arguments for creating the user.
* @param {Object} args.profile - User profile information.
* @param {string} args.profile.login - User's login/username (typically email).
* @param {string} args.profile.email - User's email address.
* @param {string} args.profile.firstName - User's first name.
* @param {string} args.profile.lastName - User's last name.
* @param {string} [args.profile.displayName] - User's display name.
* @param {string} [args.profile.department] - User's department.
* @param {string} [args.profile.title] - User's job title.
* @param {string} [args.profile.manager] - User's manager.
* @param {string} [args.profile.mobilePhone] - User's mobile phone.
* @param {Object} [args.credentials] - User credentials.
* @param {Object} [args.credentials.password] - Password information.
* @param {string} [args.credentials.password.value] - Plain text password.
* @param {boolean} [args.activate=true] - Whether to activate the user immediately.
* @param {string} [args.nextLogin] - Next login behavior (changePassword, etc.).
* @returns {Promise<Object>} - The result of the user creation.
*/
const executeFunction = async ({
profile,
credentials,
activate = true,
nextLogin
}) => {
console.log('=== CREATE USER DEBUG ===');
console.log('Profile:', profile);
console.log('Has credentials:', !!credentials);
console.log('Activate:', activate);
console.log('=========================');
// Import credentials helper from manual server
const { getOktaCredentials } = await import('../../lib/tools.js');
try {
// Validate required profile fields
if (!profile) {
throw new Error('Profile object is required');
}
const requiredFields = ['login', 'email', 'firstName', 'lastName'];
const missingFields = requiredFields.filter(field => !profile[field]);
if (missingFields.length > 0) {
throw new Error(`Missing required profile fields: ${missingFields.join(', ')}`);
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(profile.email)) {
throw new Error('Invalid email format');
}
// Auto-generate display name if not provided
if (!profile.displayName) {
profile.displayName = `${profile.firstName} ${profile.lastName}`;
console.log(`Auto-generated displayName: ${profile.displayName}`);
}
const { domain, apiToken } = await getOktaCredentials();
const baseUrl = `https://${domain}`;
// Build the request body
const requestBody = {
profile: {
login: profile.login,
email: profile.email,
firstName: profile.firstName,
lastName: profile.lastName,
displayName: profile.displayName,
// Optional fields
...(profile.department && { department: profile.department }),
...(profile.title && { title: profile.title }),
...(profile.manager && { manager: profile.manager }),
...(profile.mobilePhone && { mobilePhone: profile.mobilePhone }),
...(profile.organization && { organization: profile.organization }),
...(profile.city && { city: profile.city }),
...(profile.state && { state: profile.state }),
...(profile.zipCode && { zipCode: profile.zipCode }),
...(profile.countryCode && { countryCode: profile.countryCode })
}
};
// Add credentials if provided
if (credentials) {
requestBody.credentials = {};
if (credentials.password) {
requestBody.credentials.password = {
value: credentials.password.value
};
console.log('Including password credentials');
}
if (credentials.recovery_question) {
requestBody.credentials.recovery_question = credentials.recovery_question;
}
}
// Build URL with query parameters
const url = new URL(`${baseUrl}/api/v1/users`);
// Set activation behavior
url.searchParams.append('activate', activate.toString());
if (nextLogin) {
url.searchParams.append('nextLogin', nextLogin);
}
console.log('Creating user with request body:', JSON.stringify(requestBody, null, 2));
// Execute the request
const response = await fetch(url.toString(), {
method: 'POST',
headers: {
'Authorization': `SSWS ${apiToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorText = await response.text();
let errorData;
try {
errorData = JSON.parse(errorText);
} catch {
errorData = { error: errorText };
}
// Enhanced error handling with suggestions
let errorMessage = `HTTP ${response.status}: ${JSON.stringify(errorData)}`;
let suggestion = '';
if (response.status === 400) {
if (errorText.includes('login')) {
suggestion = 'The login/username already exists or is invalid. Try a different login.';
} else if (errorText.includes('email')) {
suggestion = 'The email address is already in use or invalid format.';
} else if (errorText.includes('password')) {
suggestion = 'Password does not meet complexity requirements. Try a stronger password.';
}
} else if (response.status === 403) {
suggestion = 'Insufficient permissions to create users. Check your API token permissions.';
}
throw new Error(`${errorMessage}${suggestion ? ` Suggestion: ${suggestion}` : ''}`);
}
const userData = await response.json();
console.log(`✅ User created successfully: ${userData.profile.login}`);
console.log(` - Status: ${userData.status}`);
console.log(` - ID: ${userData.id}`);
// Return clean, organized response
return {
id: userData.id,
status: userData.status,
created: userData.created,
activated: userData.activated,
lastUpdated: userData.lastUpdated,
profile: {
login: userData.profile.login,
email: userData.profile.email,
firstName: userData.profile.firstName,
lastName: userData.profile.lastName,
displayName: userData.profile.displayName,
department: userData.profile.department,
title: userData.profile.title,
manager: userData.profile.manager,
mobilePhone: userData.profile.mobilePhone
},
credentials: userData.credentials ? {
provider: userData.credentials.provider
} : undefined,
_links: userData._links,
message: `User '${userData.profile.login}' created successfully with status '${userData.status}'`
};
} catch (error) {
console.error('Error creating user:', error);
return {
error: `Failed to create user: ${error.message}`,
suggestion: error.message.includes('Suggestion:') ?
undefined :
"Check that all required fields are provided and that the login/email doesn't already exist."
};
}
};
/**
* Tool configuration for creating users in Okta.
* @type {Object}
*/
const apiTool = {
function: executeFunction,
definition: {
type: 'function',
function: {
name: 'create_user',
description: 'Create a new user in Okta with comprehensive profile information and optional credentials. Supports automatic activation.',
parameters: {
type: 'object',
properties: {
profile: {
type: 'object',
description: 'User profile information',
properties: {
login: {
type: 'string',
description: 'User\'s unique login identifier (typically email address)'
},
email: {
type: 'string',
format: 'email',
description: 'User\'s email address'
},
firstName: {
type: 'string',
description: 'User\'s first name'
},
lastName: {
type: 'string',
description: 'User\'s last name'
},
displayName: {
type: 'string',
description: 'User\'s display name (auto-generated if not provided)'
},
department: {
type: 'string',
description: 'User\'s department'
},
title: {
type: 'string',
description: 'User\'s job title'
},
manager: {
type: 'string',
description: 'User\'s manager'
},
mobilePhone: {
type: 'string',
description: 'User\'s mobile phone number'
},
organization: {
type: 'string',
description: 'User\'s organization'
},
city: {
type: 'string',
description: 'User\'s city'
},
state: {
type: 'string',
description: 'User\'s state or province'
},
zipCode: {
type: 'string',
description: 'User\'s ZIP or postal code'
},
countryCode: {
type: 'string',
description: 'User\'s country code'
}
},
required: ['login', 'email', 'firstName', 'lastName']
},
credentials: {
type: 'object',
description: 'User credentials (optional)',
properties: {
password: {
type: 'object',
description: 'Password information',
properties: {
value: {
type: 'string',
description: 'Plain text password'
}
},
required: ['value']
},
recovery_question: {
type: 'object',
description: 'Security question for password recovery',
properties: {
question: {
type: 'string',
description: 'Recovery question'
},
answer: {
type: 'string',
description: 'Answer to recovery question'
}
},
required: ['question', 'answer']
}
}
},
activate: {
type: 'boolean',
description: 'Whether to activate the user immediately (default: true)',
default: true
},
nextLogin: {
type: 'string',
description: 'Behavior on next login',
enum: ['changePassword']
}
},
required: ['profile']
}
}
}
};
export { apiTool };