@auth0/auth0-mcp-server
Version:
Auth0 Model Context Protocol (MCP) Server (Beta) — A secure and extendable implementation of an MCP server that provides AI assistants with controlled access to the Auth0 Management API through natural language. This project is in beta and not intended fo
619 lines • 27.2 kB
JavaScript
import { log } from '../utils/logger.js';
import { createErrorResponse, createSuccessResponse } from '../utils/http-utility.js';
import { getManagementClient } from '../utils/auth0-client.js';
// Define all available action tools
export const ACTION_TOOLS = [
{
name: 'auth0_list_actions',
description: 'List all actions in the Auth0 tenant',
inputSchema: {
type: 'object',
properties: {
page: { type: 'number', description: 'Page number (0-based)' },
per_page: { type: 'number', description: 'Number of actions per page' },
include_totals: { type: 'boolean', description: 'Include total count' },
trigger_id: { type: 'string', description: 'Filter by trigger ID' },
},
},
_meta: {
requiredScopes: ['read:actions'],
readOnly: true,
},
annotations: {
title: 'List Auth0 Actions',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_get_action',
description: 'Get details about a specific Auth0 action',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'ID of the action to retrieve' },
},
required: ['id'],
},
_meta: {
requiredScopes: ['read:actions'],
readOnly: true,
},
annotations: {
title: 'Get Auth0 Action Details',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_create_action',
description: 'Create a new Auth0 action',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the action. Required.',
},
supported_triggers: {
type: 'array',
description: 'Triggers this action handles. Required. One entry only.',
items: {
type: 'object',
properties: {
id: {
type: 'string',
enum: [
'post-login',
'credentials-exchange',
'pre-user-registration',
'post-user-registration',
'post-change-password',
'send-phone-message',
'custom-phone-provider',
'custom-email-provider',
'custom-token-exchange',
'password-reset-post-challenge',
'event-stream',
'password-hash-migration',
'login-post-identifier',
'signup-post-identifier',
],
description: 'Trigger ID. Use exact hyphenated values — e.g. post-login, not login or onLogin.',
},
version: {
type: 'string',
description: 'Version of the trigger, e.g. "v3".',
},
},
required: ['id', 'version'],
},
},
code: {
type: 'string',
description: 'JavaScript source code of the action. Required.',
},
runtime: {
type: 'string',
description: 'Node.js runtime for this action. Use node22 (current default). ' +
'node18 is supported on most tenants. Avoid node16 — deprecated and unavailable on most tenants.',
},
dependencies: {
type: 'array',
description: 'NPM modules this action depends on.',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Package name.' },
version: { type: 'string', description: 'Package version.' },
},
required: ['name', 'version'],
},
},
secrets: {
type: 'array',
description: 'Secrets available to the action.',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Secret name.' },
value: { type: 'string', description: 'Secret value.' },
},
required: ['name', 'value'],
},
},
},
required: ['name', 'supported_triggers', 'code'],
},
_meta: {
requiredScopes: ['create:actions'],
},
annotations: {
title: 'Create Auth0 Action',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: false,
},
},
{
name: 'auth0_update_action',
description: 'Update an existing Auth0 action',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'ID of the action to update. Required.',
},
name: {
type: 'string',
description: 'New name of the action. Optional.',
},
supported_triggers: {
type: 'array',
description: 'Triggers this action handles. One entry only.',
items: {
type: 'object',
properties: {
id: {
type: 'string',
enum: [
'post-login',
'credentials-exchange',
'pre-user-registration',
'post-user-registration',
'post-change-password',
'send-phone-message',
'custom-phone-provider',
'custom-email-provider',
'custom-token-exchange',
'password-reset-post-challenge',
'event-stream',
'password-hash-migration',
'login-post-identifier',
'signup-post-identifier',
],
description: 'Trigger ID. Use exact hyphenated values — e.g. post-login, not login or onLogin.',
},
version: {
type: 'string',
description: 'Version of the trigger, e.g. "v3".',
},
},
required: ['id', 'version'],
},
},
code: {
type: 'string',
description: 'JavaScript source code of the action.',
},
runtime: {
type: 'string',
description: 'Node.js runtime for this action. Use node22 (current default). ' +
'node18 is supported on most tenants. Avoid node16 — deprecated and unavailable on most tenants.',
},
dependencies: {
type: 'array',
description: 'Updated NPM dependencies.',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Package name.' },
version: { type: 'string', description: 'Package version.' },
},
required: ['name', 'version'],
},
},
secrets: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the secret variable',
},
value: {
type: 'string',
description: 'Value of the secret. If omitted, the existing value is retained.',
},
},
required: ['name'],
},
description: 'Secrets to update for the action. Optional.',
},
},
required: ['id'],
},
_meta: {
requiredScopes: ['update:actions'],
},
annotations: {
title: 'Update Auth0 Action',
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_deploy_action',
description: 'Deploy an Auth0 action to make it live. ' +
'The action must have been successfully created and built. ' +
'If the action was just created with invalid parameters, the build may have failed — ' +
'verify with auth0_get_action before deploying.',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'ID of the action to deploy' },
},
required: ['id'],
},
_meta: {
requiredScopes: ['update:actions'],
},
annotations: {
title: 'Deploy Auth0 Action',
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: false,
},
},
];
// Define handlers for each action tool
export const ACTION_HANDLERS = {
auth0_list_actions: async (request, config) => {
try {
// Check for token
if (!request.token) {
log('Warning: Token is empty or undefined');
return createErrorResponse('Error: Missing authorization token');
}
// Check if domain is configured
if (!config.domain) {
log('Error: Auth0 domain is not configured');
return createErrorResponse('Error: Auth0 domain is not configured');
}
// Build query parameters
const options = {};
if (request.parameters.page !== undefined) {
options.page = request.parameters.page;
}
if (request.parameters.per_page !== undefined) {
options.per_page = request.parameters.per_page;
}
else {
// Default to 5 items per page
options.per_page = 5;
}
if (request.parameters.include_totals !== undefined) {
options.include_totals = request.parameters.include_totals;
}
else {
// Default to include totals
options.include_totals = true;
}
if (request.parameters.trigger_id) {
options.triggerId = request.parameters.trigger_id;
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
// Use the Auth0 SDK to get all actions
const { data: responseData } = await managementClient.actions.getAll(options);
// Handle different response formats
let actions = [];
let total = 0;
let page = 0;
let perPage = options.per_page || 5;
if (Array.isArray(responseData)) {
// Simple array response
actions = responseData;
total = actions.length;
}
else if (typeof responseData === 'object' &&
responseData !== null &&
'actions' in responseData &&
Array.isArray(responseData.actions)) {
// Paginated response with totals
actions = responseData.actions;
total = responseData.total || actions.length;
page = responseData.page || 0;
perPage = responseData.per_page || actions.length;
}
else {
log('Invalid response format');
return createErrorResponse('Error: Received invalid response format from Auth0 API.');
}
if (actions.length === 0) {
return createSuccessResponse({
message: 'No actions found in your Auth0 tenant.',
actions: [],
});
}
// Create a result object with all the necessary information
const result = {
actions: actions,
count: actions.length,
total: total,
pagination: {
page: page,
per_page: perPage,
total_pages: Math.ceil(total / perPage),
has_next: page + 1 < Math.ceil(total / perPage),
},
};
log(`Successfully retrieved actions`);
return createSuccessResponse(result);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to list actions: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing read:actions scope.';
}
else if (sdkError.statusCode === 403) {
errorMessage +=
'\nError: Forbidden. Your token might not have the required scopes (read:actions).';
}
else if (sdkError.statusCode === 429) {
errorMessage +=
'\nError: Rate limited. You have made too many requests to the Auth0 API. Please try again later.';
}
else if (sdkError.statusCode >= 500) {
errorMessage +=
'\nError: Auth0 server error. The Auth0 API might be experiencing issues. Please try again later.';
}
return createErrorResponse(errorMessage);
}
}
catch (error) {
// Handle any other errors
log('Error processing request');
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
}
},
auth0_get_action: async (request, config) => {
try {
const id = request.parameters.id;
if (!id) {
return createErrorResponse('Error: id is required');
}
// Check for token
if (!request.token) {
log('Warning: Token is empty or undefined');
return createErrorResponse('Error: Missing authorization token');
}
// Check if domain is configured
if (!config.domain) {
log('Error: Auth0 domain is not configured');
return createErrorResponse('Error: Auth0 domain is not configured');
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Fetching action with ID: ${id}`);
// Use the Auth0 SDK to get a specific action
const { data: action } = await managementClient.actions.get({ id });
log(`Successfully retrieved action: ${action.name || 'Unknown'} (${action.id || id})`);
return createSuccessResponse(action);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to get action: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 404) {
errorMessage = `Action with id '${id}' not found.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing read:actions scope.';
}
return createErrorResponse(errorMessage);
}
}
catch (error) {
// Handle any other errors
log('Error processing request');
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
}
},
auth0_create_action: async (request, config) => {
try {
const { name, supported_triggers, code, runtime = 'node18', dependencies = [], secrets = [], } = request.parameters;
if (!name) {
return createErrorResponse('Error: name is required');
}
if (!supported_triggers ||
!Array.isArray(supported_triggers) ||
supported_triggers.length === 0) {
return createErrorResponse('Error: supported_triggers is required and must be a non-empty array');
}
if (!code) {
return createErrorResponse('Error: code is required');
}
// Check for token
if (!request.token) {
log('Warning: Token is empty or undefined');
return createErrorResponse('Error: Missing authorization token');
}
// Check if domain is configured
if (!config.domain) {
log('Error: Auth0 domain is not configured');
return createErrorResponse('Error: Auth0 domain is not configured');
}
// Prepare request body
const actionData = {
name,
supported_triggers,
code,
runtime,
dependencies,
secrets,
};
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Creating new action with name: ${name}`);
// Use the Auth0 SDK to create an action
const { data: newAction } = await managementClient.actions.create(actionData);
log(`Successfully created action: ${newAction.name || name} (${newAction.id || 'new action'})`);
return createSuccessResponse(newAction);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to create action: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing create:actions scope.';
}
else if (sdkError.statusCode === 422) {
errorMessage +=
'\nError: Validation errors in your request. Check that your parameters are valid.';
}
return createErrorResponse(errorMessage);
}
}
catch (error) {
// Handle any other errors
log('Error processing request');
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
}
},
auth0_update_action: async (request, config) => {
try {
const id = request.parameters.id;
if (!id) {
return createErrorResponse('Error: id is required');
}
// Extract other parameters to update
const { name, supported_triggers, code, runtime, dependencies, secrets } = request.parameters;
// Prepare update body, only including fields that are present
const updateData = {};
if (name !== undefined)
updateData.name = name;
if (supported_triggers !== undefined)
updateData.supported_triggers = supported_triggers;
if (code !== undefined)
updateData.code = code;
if (runtime !== undefined)
updateData.runtime = runtime;
if (dependencies !== undefined)
updateData.dependencies = dependencies;
if (secrets !== undefined)
updateData.secrets = secrets;
// Check for token
if (!request.token) {
log('Warning: Token is empty or undefined');
return createErrorResponse('Error: Missing authorization token');
}
// Check if domain is configured
if (!config.domain) {
log('Error: Auth0 domain is not configured');
return createErrorResponse('Error: Auth0 domain is not configured');
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Updating action with ID: ${id}`);
// Use the Auth0 SDK to update the action
const { data: updatedAction } = await managementClient.actions.update({ id }, updateData);
// Add information about secrets update to the result
const result = {
...updatedAction,
};
log(`Successfully updated action: ${result.name || 'Unknown'} (${result.id || id})`);
return createSuccessResponse(result);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to update action: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 404) {
errorMessage = `Action with id '${id}' not found.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing update:actions scope.';
}
else if (sdkError.statusCode === 422) {
errorMessage +=
'\nError: Validation errors in your request. Check that your parameters are valid.';
}
return createErrorResponse(errorMessage);
}
}
catch (error) {
// Handle any other errors
log('Error processing request');
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
}
},
auth0_deploy_action: async (request, config) => {
try {
const id = request.parameters.id;
if (!id) {
return createErrorResponse('Error: id is required');
}
// Check for token
if (!request.token) {
log('Warning: Token is empty or undefined');
return createErrorResponse('Error: Missing authorization token');
}
// Check if domain is configured
if (!config.domain) {
log('Error: Auth0 domain is not configured');
return createErrorResponse('Error: Auth0 domain is not configured');
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Deploying action with ID: ${id}`);
// Use the Auth0 SDK to deploy the action
const { data: deployedAction } = await managementClient.actions.deploy({ id });
log(`Successfully deployed action: ${deployedAction.name || 'Unknown'} (${deployedAction.id || id})`);
return createSuccessResponse(deployedAction);
}
catch (error) {
// Handle SDK errors
log('Error deploying action:', error);
const errorMessage = `Failed to deploy action: ${error.message || 'Unknown error'}`;
return createErrorResponse(errorMessage);
}
}
catch (error) {
// Handle any other errors
log('Error processing request');
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
}
},
};
//# sourceMappingURL=actions.js.map