UNPKG

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