@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
122 lines • 5.74 kB
JavaScript
import { startServer } from '../server.js';
import trackEvent from '../utils/analytics.js';
import { log, logError, logInfo } from '../utils/logger.js';
import { validatePatterns } from '../utils/tools.js';
import { TOOLS } from '../tools/index.js';
import * as os from 'os';
import { keychain } from '../utils/keychain.js';
import { isTokenExpired } from '../auth/device-auth-flow.js';
import chalk from 'chalk';
/**
* Validates authorization preconditions before starting the server
*
* This function provides user-friendly validation with detailed CLI output
* and actionable guidance for resolving authentication issues. It forms the
* first layer of the MCP server's validation architecture:
*
* 1. Initial validation (here): Provides rich user feedback during startup
* 2. Server startup validation: Secondary checkpoint in `server.ts`
* 3. Continuous validation: During tool calls via the `validateConfig()` function
*
* Each layer serves a distinct purpose, creating a balance between security
* and developer experience. This function focuses on DX with detailed,
* human-readable feedback, while later validation layers provide ongoing
* security with more technical checks.
*
* @returns {Promise<boolean>} True if authorization is valid, false otherwise
*/
const validateAuthorization = async () => {
// Check if token exists
const token = await keychain.getToken();
if (!token) {
logError(`${chalk.red('Authorization Error:')} No valid authorization token found`);
logError(`${chalk.bold('Recommended actions:')}`);
logError(`1. Run ${chalk.cyan('npx @auth0/auth0-mcp-server init')} to authorize with Auth0`);
logError(`2. Use ${chalk.cyan('npx @auth0/auth0-mcp-server session')} to check your current session status`);
return false;
}
// Check if token is expired
const expired = await isTokenExpired();
if (expired) {
const expiresAt = await keychain.getTokenExpiresAt();
const expiryDate = expiresAt ? new Date(expiresAt).toLocaleString() : 'unknown';
logError(`${chalk.red('Authorization Error:')} Token has expired (on ${expiryDate})`);
logError(`${chalk.bold('Recommended actions:')}`);
logError(`1. Run ${chalk.cyan('npx @auth0/auth0-mcp-server init')} to refresh your authorization`);
logError(`2. Use ${chalk.cyan('npx @auth0/auth0-mcp-server session')} to check your current session details`);
return false;
}
// Check if domain exists
const domain = await keychain.getDomain();
if (!domain) {
logError(`${chalk.red('Authorization Error:')} No Auth0 domain found in configuration`);
logError(`${chalk.bold('Recommended actions:')}`);
logError(`1. Run ${chalk.cyan('npx @auth0/auth0-mcp-server init')} to authorize with Auth0`);
logError(`2. Use ${chalk.cyan('npx @auth0/auth0-mcp-server session')} to check your current configuration`);
return false;
}
return true;
};
/**
* Main function to start server
*
* @param {RunOptions} options - Command options
* @returns {Promise<void>}
*/
const run = async (options) => {
try {
if (!process.env.HOME) {
process.env.HOME = os.homedir();
log(`Set HOME environment variable to ${process.env.HOME}`);
}
// Honor env vars for MCPB-style installs where CLI args are not passed through
if (!options.readOnly && process.env.AUTH0_MCP_READ_ONLY === 'true') {
options.readOnly = true;
}
const defaultTools = options.tools.length === 1 && options.tools[0] === '*';
if (defaultTools && process.env.AUTH0_MCP_TOOLS) {
options.tools = process.env.AUTH0_MCP_TOOLS.split(',')
.map((t) => t.trim())
.filter(Boolean);
if (options.tools.length === 0) {
options.tools = ['*'];
}
// Validate patterns same way as CLI path, but skip validation for wildcard
if (!(options.tools.length === 1 && options.tools[0] === '*')) {
validatePatterns(options.tools, TOOLS);
}
}
trackEvent.trackServerRun();
// Check if AUTH0_TOKEN is set without AUTH0_DOMAIN
if (process.env.AUTH0_TOKEN && !process.env.AUTH0_DOMAIN) {
logError('AUTH0_TOKEN is set but AUTH0_DOMAIN is required. Set AUTH0_DOMAIN to your Auth0 tenant domain (e.g. your-tenant.auth0.com)');
process.exit(1);
}
// Skip keychain-based validation when credentials are provided via env (MCPB-style launch).
// Startup validation in server.ts and per-tool validation still enforce correctness.
const envAuth = Boolean(process.env.AUTH0_TOKEN && process.env.AUTH0_DOMAIN);
if (!envAuth) {
const isAuthorized = await validateAuthorization();
if (!isAuthorized) {
// Exit with code 1 (standard error code)
process.exit(1);
}
}
if (options.readOnly && options.tools.length === 1 && options.tools[0] === '*') {
logInfo('Starting server in read-only mode');
}
else if (options.readOnly) {
logInfo(`Starting server in read-only mode with tools matching the following pattern(s): ${options.tools.join(', ')} (--read-only has priority)`);
}
else {
logInfo(`Starting server with tools matching the following pattern(s): ${options.tools.join(', ')}`);
}
await startServer(options);
}
catch (error) {
logError('Fatal error starting server:', error);
process.exit(1);
}
};
export default run;
//# sourceMappingURL=run.js.map