@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
466 lines • 20.8 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 form tools
export const FORM_TOOLS = [
{
name: 'auth0_list_forms',
description: 'List all forms in the Auth0 tenant',
inputSchema: {
type: 'object',
properties: {
page: { type: 'number', description: 'Page number (0-based)' },
per_page: { type: 'number', description: 'Number of forms per page default:50' },
include_totals: { type: 'boolean', description: 'Include total count' },
},
},
_meta: {
requiredScopes: ['read:forms'],
readOnly: true,
},
annotations: {
title: 'List Auth0 Forms',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_get_form',
description: 'Get details about a specific Auth0 form',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'ID of the form to retrieve' },
},
required: ['id'],
},
_meta: {
requiredScopes: ['read:forms'],
readOnly: true,
},
annotations: {
title: 'Get Auth0 Form Details',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_create_form',
description: 'Create a new Auth0 form',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1,
maxLength: 150,
description: 'Form name. Required. Maximum 150 characters.',
},
messages: {
type: 'object',
description: 'Message settings for the form. IMPORTANT: the Auth0 Forms API enforces strict field validation ' +
'(additionalProperties: false). Only send fields defined in the Auth0 Forms API spec.',
},
languages: {
type: 'object',
description: 'Language settings for the form. IMPORTANT: strict field validation enforced — ' +
'only send fields defined in the Auth0 Forms API spec.',
},
translations: {
type: 'object',
description: 'Locale translations for form content. ' +
'Outer keys are locale codes (e.g. "fr", "es", "pt-BR"). ' +
'Inner keys are translation string identifiers with their translated values.',
},
nodes: {
type: 'array',
description: 'Form nodes defining structure and routing. IMPORTANT: the Auth0 Forms API enforces strict field ' +
'validation (additionalProperties: false) at every level. Each node must be one of: FormFlow, ' +
'FormRouter, or FormStep with exact required fields. Unknown fields at any depth cause a 400 error.',
items: {
type: 'object',
},
},
start: {
type: 'object',
description: 'Settings for form start configuration. IMPORTANT: strict field validation enforced — ' +
'only send fields defined in the Auth0 Forms API spec.',
},
ending: {
type: 'object',
description: 'Settings for form completion. IMPORTANT: strict field validation enforced — ' +
'only send fields defined in the Auth0 Forms API spec.',
},
style: {
type: 'object',
additionalProperties: false,
description: 'CSS styling for the form. Only the css field is accepted — do not send color, theme, font, or other properties.',
properties: {
css: {
type: 'string',
minLength: 1,
maxLength: 5000,
description: 'Raw CSS string for the form. This is the only accepted style field.',
},
},
},
},
required: ['name'],
},
_meta: {
requiredScopes: ['create:forms'],
},
annotations: {
title: 'Create Auth0 Form',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: false,
},
},
{
name: 'auth0_update_form',
description: 'Update an existing Auth0 form',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'ID of the form to update. Required.',
},
name: {
type: 'string',
minLength: 1,
maxLength: 150,
description: 'Form name. Maximum 150 characters.',
},
messages: {
type: 'object',
description: 'Message settings for the form. IMPORTANT: the Auth0 Forms API enforces strict field validation ' +
'(additionalProperties: false). Only send fields defined in the Auth0 Forms API spec.',
},
languages: {
type: 'object',
description: 'Language settings for the form. IMPORTANT: strict field validation enforced — ' +
'only send fields defined in the Auth0 Forms API spec.',
},
translations: {
type: 'object',
description: 'Locale translations for form content. ' +
'Outer keys are locale codes (e.g. "fr", "es", "pt-BR"). ' +
'Inner keys are translation string identifiers with their translated values.',
},
nodes: {
type: 'array',
description: 'Form nodes defining structure and routing. IMPORTANT: the Auth0 Forms API enforces strict field ' +
'validation (additionalProperties: false) at every level. Each node must be one of: FormFlow, ' +
'FormRouter, or FormStep with exact required fields. Unknown fields at any depth cause a 400 error.',
items: {
type: 'object',
},
},
start: {
type: 'object',
description: 'Settings for form start configuration. IMPORTANT: strict field validation enforced — ' +
'only send fields defined in the Auth0 Forms API spec.',
},
ending: {
type: 'object',
description: 'Settings for form completion. IMPORTANT: strict field validation enforced — ' +
'only send fields defined in the Auth0 Forms API spec.',
},
style: {
type: 'object',
description: 'CSS styling for the form. Include this field to apply custom CSS. Only the css field is accepted.',
properties: {
css: {
type: 'string',
description: 'Raw CSS string for the form.',
},
},
},
},
required: ['id'],
},
_meta: {
requiredScopes: ['update:forms'],
},
annotations: {
title: 'Update Auth0 Form',
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false,
},
},
];
// Define handlers for each form tool
export const FORM_HANDLERS = {
auth0_list_forms: 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 50 forms per page
options.per_page = 50;
}
if (request.parameters.include_totals !== undefined) {
options.include_totals = request.parameters.include_totals;
}
else {
// Default to include totals
options.include_totals = true;
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Fetching forms with supplied options`);
// Use the Auth0 SDK to get all forms
const { data: responseData } = await managementClient.forms.getAll(options);
// Handle different response formats
log(`Successfully retrieved forms`);
if (!responseData) {
return createSuccessResponse({
message: 'No forms found matching your criteria.',
forms: [],
});
}
return createSuccessResponse(responseData);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to list forms: ${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:branding scope.';
}
else if (sdkError.statusCode === 403) {
errorMessage +=
'\nError: Forbidden. Your token might not have the required scopes (read:branding).';
}
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_form: 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 form with ID: ${id}`);
// Use the Auth0 SDK to get a specific form
const { data: form } = await managementClient.forms.get({ id });
log(`Successfully retrieved form: ${form.name} (${form.id})`);
return createSuccessResponse(form);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to get form: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 404) {
errorMessage = `Form with id '${id}' not found.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing read:branding 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_form: async (request, config) => {
try {
const { name, messages, languages, translations, nodes, start, ending, style } = request.parameters;
if (!name) {
return createErrorResponse('Error: name 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 with required properties according to PostFormsRequest interface
const formData = {
name: name,
};
// Add optional properties if defined
if (messages !== undefined)
formData.messages = messages;
if (languages !== undefined)
formData.languages = languages;
if (translations !== undefined)
formData.translations = translations;
if (nodes !== undefined)
formData.nodes = nodes;
if (start !== undefined)
formData.start = start;
if (ending !== undefined)
formData.ending = ending;
if (style !== undefined)
formData.style = style;
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Creating new form with name: ${name}`);
// Use the Auth0 SDK to create a form
const { data: newForm } = await managementClient.forms.create(formData);
log(`Successfully created form: ${newForm.name || name} (${newForm.id || 'new form'})`);
return createSuccessResponse(newForm);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to create form: ${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:branding 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_form: async (request, config) => {
try {
const { id, name, messages, languages, translations, nodes, start, ending, style } = request.parameters;
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');
}
// Prepare request body - partial PostFormsRequest
const updateData = {};
// Add properties if defined
if (name !== undefined)
updateData.name = name;
if (messages !== undefined)
updateData.messages = messages;
if (languages !== undefined)
updateData.languages = languages;
if (translations !== undefined)
updateData.translations = translations;
if (nodes !== undefined)
updateData.nodes = nodes;
if (start !== undefined)
updateData.start = start;
if (ending !== undefined)
updateData.ending = ending;
if (style !== undefined)
updateData.style = style;
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Updating form with ID: ${id}`);
// Use the Auth0 SDK to update a form
const { data: updatedForm } = await managementClient.forms.update({ id }, updateData);
log(`Successfully updated form: ${updatedForm.name || 'Unknown'} (${updatedForm.id || id})`);
return createSuccessResponse(updatedForm);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to update form: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 404) {
errorMessage = `Form with id '${id}' not found.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing update:branding 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)}`);
}
},
};
//# sourceMappingURL=forms.js.map