@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
613 lines • 28.6 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 resource server tools
export const RESOURCE_SERVER_TOOLS = [
{
name: 'auth0_list_resource_servers',
description: 'List all resource servers (APIs) in the Auth0 tenant',
inputSchema: {
type: 'object',
properties: {
page: { type: 'number', description: 'Page number (0-based)' },
per_page: { type: 'number', description: 'Number of resource servers per page' },
include_totals: { type: 'boolean', description: 'Include total count' },
identifiers: {
type: 'array',
items: { type: 'string' },
description: 'A list of URI encoded identifiers to filter the results by. Consider URL limits when using this parameter.',
},
include_fields: {
type: 'boolean',
description: 'Whether specified fields are to be included (true) or excluded (false)',
},
},
},
_meta: {
requiredScopes: ['read:resource_servers'],
readOnly: true,
},
annotations: {
title: 'List Auth0 Resource Servers',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_get_resource_server',
description: 'Get details about a specific Auth0 resource server',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'ID of the resource server to retrieve' },
},
required: ['id'],
},
_meta: {
requiredScopes: ['read:resource_servers'],
readOnly: true,
},
annotations: {
title: 'Get Auth0 Resource Server Details',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
{
name: 'auth0_create_resource_server',
description: 'Create a new Auth0 resource server (API). Use RS256 for the signing_alg unless otherwise specified.',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Friendly name for the resource server. Required.',
},
identifier: {
type: 'string',
description: 'Unique identifier for the API (e.g., https://api.example.com). Required.',
},
scopes: {
type: 'array',
description: 'List of permissions (scopes) that this API uses.',
items: {
type: 'object',
properties: {
value: { type: 'string' },
description: { type: 'string' },
},
},
},
signing_alg: {
type: 'string',
description: 'Algorithm used to sign JWTs. Can be HS256 or RS256.',
enum: ['HS256', 'RS256', 'PS256'],
},
signing_secret: {
type: 'string',
description: 'Secret used to sign tokens when using symmetric algorithms (HS256).',
},
allow_offline_access: {
type: 'boolean',
description: 'Whether refresh tokens can be issued for this API.',
},
token_lifetime: {
type: 'number',
description: 'Expiration value (in seconds) for access tokens.',
},
token_dialect: {
type: 'string',
description: 'Dialect of issued access token.',
},
skip_consent_for_verifiable_first_party_clients: {
type: 'boolean',
description: 'Whether to skip user consent for applications flagged as first party.',
},
enforce_policies: {
type: 'boolean',
description: 'Whether to enforce authorization policies.',
},
client: {
type: 'object',
description: 'Client-related configuration for the resource server.',
},
token_encryption: {
type: 'object',
description: 'Token encryption configuration.',
},
consent_policy: {
type: 'string',
description: 'Policy for obtaining consent.',
},
authorization_details: {
anyOf: [
{
type: 'array',
items: {
type: 'object',
description: 'The valid authorization_detail definition',
},
},
{
type: 'null',
},
],
description: 'Authorization details for the resource server.',
},
proof_of_possession: {
type: 'object',
description: 'Proof of possession configuration.',
},
},
required: ['name', 'identifier'],
},
_meta: {
requiredScopes: ['create:resource_servers'],
},
annotations: {
title: 'Create Auth0 Resource Server',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: false,
},
},
{
name: 'auth0_update_resource_server',
description: 'Update an existing Auth0 resource server',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'ID of the resource server to update. Required.',
},
name: {
type: 'string',
description: 'New friendly name for the resource server.',
},
scopes: {
type: 'array',
description: 'List of permissions (scopes) that this API uses.',
items: {
type: 'object',
properties: {
value: { type: 'string' },
description: { type: 'string' },
},
},
},
signing_alg: {
type: 'string',
description: 'Algorithm used to sign JWTs. Can be HS256 or RS256.',
enum: ['HS256', 'RS256', 'PS256'],
},
signing_secret: {
type: 'string',
description: 'Secret used to sign tokens when using symmetric algorithms (HS256).',
},
allow_offline_access: {
type: 'boolean',
description: 'Whether refresh tokens can be issued for this API.',
},
token_lifetime: {
type: 'number',
description: 'Expiration value (in seconds) for access tokens.',
},
token_dialect: {
type: 'string',
description: 'Dialect of issued access token.',
},
skip_consent_for_verifiable_first_party_clients: {
type: 'boolean',
description: 'Whether to skip user consent for applications flagged as first party.',
},
enforce_policies: {
type: 'boolean',
description: 'Whether to enforce authorization policies.',
},
client: {
type: 'object',
description: 'Client-related configuration for the resource server.',
},
token_encryption: {
type: 'object',
description: 'Token encryption configuration.',
},
consent_policy: {
type: 'string',
description: 'Policy for obtaining consent.',
},
authorization_details: {
anyOf: [
{
type: 'array',
items: {
type: 'object',
description: 'The valid authorization_detail definition',
},
},
{
type: 'null',
},
],
description: 'Authorization details for the resource server.',
},
proof_of_possession: {
type: 'object',
description: 'Proof of possession configuration.',
},
},
required: ['id'],
},
_meta: {
requiredScopes: ['update:resource_servers'],
},
annotations: {
title: 'Update Auth0 Resource Server',
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false,
},
},
];
// Define handlers for each resource server tool
export const RESOURCE_SERVER_HANDLERS = {
auth0_list_resource_servers: async (request, config) => {
try {
if (!request.token) {
log('Warning: Token is missing');
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 {
options.per_page = 5;
}
if (request.parameters.include_totals !== undefined) {
options.include_totals = request.parameters.include_totals;
}
else {
options.include_totals = true;
}
// Add new parameters
if (request.parameters.identifiers !== undefined) {
options.identifiers = request.parameters.identifiers;
}
if (request.parameters.include_fields !== undefined) {
options.include_fields = request.parameters.include_fields;
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Fetching resource servers with supplied options`);
// Use the Auth0 SDK to get all resource servers
const { data: responseData } = await managementClient.resourceServers.getAll(options);
if (!responseData ||
(typeof responseData === 'object' &&
!('resource_servers' in responseData) &&
!Array.isArray(responseData))) {
log('Invalid response format - missing resource_servers array');
return createErrorResponse('Error: Received invalid response format from Auth0 API. The "resource_servers" array is missing or invalid.');
}
// Format resource servers list
const resourceServers = Array.isArray(responseData)
? responseData.map(formatResourceServer)
: (responseData?.resource_servers || []).map(formatResourceServer);
// Get pagination info
const total = Array.isArray(responseData)
? resourceServers.length
: responseData?.total || resourceServers.length;
const page = Array.isArray(responseData)
? 0
: responseData?.page !== undefined
? responseData.page
: 0;
const perPage = Array.isArray(responseData)
? resourceServers.length
: responseData?.per_page || resourceServers.length;
const totalPages = Math.ceil(total / perPage);
log(`Successfully retrieved ${resourceServers.length} resource servers (page ${(page || 0) + 1} of ${totalPages}, total: ${total})`);
// Create a result object with all the necessary information
const result = {
resource_servers: resourceServers,
count: resourceServers.length,
total: total,
pagination: {
page: page || 0,
per_page: perPage,
total_pages: totalPages,
has_next: (page || 0) + 1 < totalPages,
},
};
return createSuccessResponse(result);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to list resource servers: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error scenarios
if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing read:resource_servers scope.';
}
else if (sdkError.statusCode === 403) {
errorMessage +=
'\nError: Forbidden. Your token might not have the required scopes (read:resource_servers).';
}
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_resource_server: 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 resource server with ID: ${id}`);
// Use the Auth0 SDK to get a specific resource server
const { data: resourceServer } = await managementClient.resourceServers.get({ id });
log(`Successfully retrieved resource server: ${resourceServer.name || 'Unknown'} (${resourceServer.id || id})`);
return createSuccessResponse(resourceServer);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to get resource server: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 404) {
errorMessage = `Resource server with id '${id}' not found.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing read:resource_servers 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_resource_server: async (request, config) => {
try {
// Get request parameters
const { name, identifier, scopes, signing_alg, signing_secret, token_lifetime, allow_offline_access, token_dialect, skip_consent_for_verifiable_first_party_clients, enforce_policies, client, token_encryption, consent_policy, authorization_details, proof_of_possession, } = request.parameters;
// Validate required fields
if (!identifier) {
return createErrorResponse('Error: identifier is required');
}
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');
}
try {
const managementClientConfig = {
domain: config.domain,
token: request.token,
};
const managementClient = await getManagementClient(managementClientConfig, config.headers);
log(`Creating resource server with identifier: ${identifier}`);
// Prepare the resource server data
const resourceServerData = {
name,
identifier,
};
// Add optional fields if provided
if (scopes !== undefined)
resourceServerData.scopes = scopes;
if (signing_alg !== undefined)
resourceServerData.signing_alg = signing_alg;
if (signing_secret !== undefined)
resourceServerData.signing_secret = signing_secret;
if (token_lifetime !== undefined)
resourceServerData.token_lifetime = token_lifetime;
if (allow_offline_access !== undefined)
resourceServerData.allow_offline_access = allow_offline_access;
if (token_dialect !== undefined)
resourceServerData.token_dialect = token_dialect;
if (skip_consent_for_verifiable_first_party_clients !== undefined)
resourceServerData.skip_consent_for_verifiable_first_party_clients =
skip_consent_for_verifiable_first_party_clients;
if (enforce_policies !== undefined)
resourceServerData.enforce_policies = enforce_policies;
if (client !== undefined)
resourceServerData.client = client;
if (token_encryption !== undefined)
resourceServerData.token_encryption = token_encryption;
if (consent_policy !== undefined)
resourceServerData.consent_policy = consent_policy;
if (authorization_details !== undefined)
resourceServerData.authorization_details = authorization_details;
if (proof_of_possession !== undefined)
resourceServerData.proof_of_possession = proof_of_possession;
// Use the Auth0 SDK to create a resource server
const { data: resourceServer } = await managementClient.resourceServers.create(resourceServerData);
log(`Successfully created resource server: ${resourceServer.name || 'Unknown'} (${resourceServer.id || 'Unknown ID'})`);
return createSuccessResponse(resourceServer);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to create resource server: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 409) {
errorMessage = `Resource server with identifier '${identifier}' already exists.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing create:resource_servers scope.';
}
else if (sdkError.statusCode === 403) {
errorMessage +=
'\nError: Forbidden. You do not have permission to create resource servers.';
}
else if (sdkError.statusCode === 429) {
errorMessage += '\nError: Too many requests. Rate limit exceeded.';
}
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_resource_server: async (request, config) => {
try {
const id = request.parameters.id;
if (!id) {
return createErrorResponse('Error: id is required');
}
// Extract other parameters to update
const { name, scopes, signing_alg, signing_secret, token_lifetime, allow_offline_access, token_dialect, skip_consent_for_verifiable_first_party_clients, enforce_policies, client, token_encryption, consent_policy, authorization_details, proof_of_possession, } = request.parameters;
// 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 resource server with ID: ${id}`);
// Prepare update body, only including fields that are present
const updateData = {};
if (name !== undefined)
updateData.name = name;
if (scopes !== undefined)
updateData.scopes = scopes;
if (signing_alg !== undefined)
updateData.signing_alg = signing_alg;
if (signing_secret !== undefined)
updateData.signing_secret = signing_secret;
if (token_lifetime !== undefined)
updateData.token_lifetime = token_lifetime;
if (allow_offline_access !== undefined)
updateData.allow_offline_access = allow_offline_access;
if (token_dialect !== undefined)
updateData.token_dialect = token_dialect;
if (skip_consent_for_verifiable_first_party_clients !== undefined)
updateData.skip_consent_for_verifiable_first_party_clients =
skip_consent_for_verifiable_first_party_clients;
if (enforce_policies !== undefined)
updateData.enforce_policies = enforce_policies;
if (client !== undefined)
updateData.client = client;
if (token_encryption !== undefined)
updateData.token_encryption = token_encryption;
if (consent_policy !== undefined)
updateData.consent_policy = consent_policy;
if (authorization_details !== undefined)
updateData.authorization_details = authorization_details;
if (proof_of_possession !== undefined)
updateData.proof_of_possession = proof_of_possession;
// Use the Auth0 SDK to update the resource server
const { data: resourceServer } = await managementClient.resourceServers.update({ id }, updateData);
log(`Successfully updated resource server: ${resourceServer.name || 'Unknown'} (${resourceServer.id || id})`);
return createSuccessResponse(resourceServer);
}
catch (sdkError) {
// Handle SDK errors
log('Auth0 SDK error');
let errorMessage = `Failed to update resource server: ${sdkError.message || 'Unknown error'}`;
// Add context based on common error codes
if (sdkError.statusCode === 404) {
errorMessage = `Resource server with id '${id}' not found.`;
}
else if (sdkError.statusCode === 401) {
errorMessage +=
'\nError: Unauthorized. Your token might be expired or invalid or missing update:resource_servers 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)}`);
}
},
};
// Helper function to format a resource server
function formatResourceServer(server) {
return {
id: server.id,
name: server.name,
identifier: server.identifier,
scopes: server.scopes?.length || 0,
};
}
//# sourceMappingURL=resource-servers.js.map