okta-mcp-server
Version:
Model Context Protocol (MCP) server for Okta API operations with support for bulk operations and caching
142 lines • 4.75 kB
JavaScript
import { z } from 'zod';
import fs from 'fs';
import path from 'path';
// Load environment variables manually to avoid dotenv stdout pollution
// This prevents the "[dotenv@17.2.1] injecting env..." message
function loadEnvFile() {
try {
const envPath = path.resolve(process.cwd(), '.env');
if (fs.existsSync(envPath)) {
const envContent = fs.readFileSync(envPath, 'utf8');
const lines = envContent.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#'))
continue;
const [key, ...valueParts] = trimmed.split('=');
if (key && valueParts.length > 0) {
const value = valueParts.join('=').replace(/^["']|["']$/g, '');
if (!process.env[key]) {
process.env[key] = value;
}
}
}
}
}
catch (error) {
// Silently ignore .env file errors
}
}
// Load environment variables without dotenv's stdout pollution
loadEnvFile();
// Environment variable schema - SIMPLIFIED
const EnvSchema = z.object({
// Mock mode configuration
USE_MOCK_OKTA: z
.union([z.string(), z.boolean()])
.optional()
.transform((v) => {
if (typeof v === 'boolean')
return v;
return v === 'true';
})
.default(false),
// Okta configuration - SIMPLIFIED to just domain and API token
OKTA_DOMAIN: z.string().optional(),
OKTA_API_TOKEN: z.string().optional(),
// Optional MCP OAuth protection
MCP_AUTH_REQUIRED: z
.string()
.transform((v) => v === 'true')
.optional()
.default('false'),
MCP_AUTH_AUDIENCE: z.string().optional(),
MCP_AUTH_ISSUER: z.string().optional(),
// Read-only mode - prevents dangerous write operations
READ_ONLY_MODE: z
.string()
.transform((v) => v === 'true')
.optional()
.default('false'),
// Server configuration
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
// Cache configuration - kept for performance
CACHE_ENABLED: z
.string()
.transform((v) => v === 'true')
.default('true'),
CACHE_TYPE: z.enum(['memory', 'sqlite']).default('memory'),
CACHE_TTL: z
.string()
.transform((v) => parseInt(v, 10))
.default('300'),
CACHE_MAX_SIZE: z
.string()
.transform((v) => parseInt(v, 10))
.default('1000'),
// Rate limiting - kept for Okta API compliance
RATE_LIMIT_MAX: z
.string()
.transform((v) => parseInt(v, 10))
.default('100'),
RATE_LIMIT_WINDOW: z
.string()
.transform((v) => parseInt(v, 10))
.default('60000'),
});
// Parse environment variables
const env = EnvSchema.parse(process.env);
// Load and validate configuration
export function loadConfig() {
// Skip validation if using mock mode
if (!env.USE_MOCK_OKTA) {
// Validate that we have API token
if (!env.OKTA_API_TOKEN) {
throw new Error('OKTA_API_TOKEN must be provided');
}
if (!env.OKTA_DOMAIN) {
throw new Error('OKTA_DOMAIN must be provided when USE_MOCK_OKTA is false');
}
}
const config = {
name: 'okta-mcp-server',
version: '2.0.0', // Major version bump for breaking changes
okta: {
domain: env.OKTA_DOMAIN || 'https://mock.okta.com',
apiToken: env.OKTA_API_TOKEN || '',
requestTimeout: 30000,
maxRetries: 3,
rateLimitPerMinute: env.RATE_LIMIT_MAX,
},
// Optional MCP OAuth protection
auth: env.MCP_AUTH_REQUIRED
? {
required: true,
audience: env.MCP_AUTH_AUDIENCE,
issuer: env.MCP_AUTH_ISSUER,
}
: undefined,
cache: {
enabled: env.CACHE_ENABLED,
type: env.CACHE_TYPE,
ttl: env.CACHE_TTL,
maxSize: env.CACHE_MAX_SIZE,
},
logging: {
level: env.LOG_LEVEL,
format: env.NODE_ENV === 'production' ? 'json' : 'pretty',
},
features: {
streaming: true,
caching: env.CACHE_ENABLED,
rateLimit: true,
audit: true,
readOnlyMode: env.READ_ONLY_MODE,
},
};
return config;
}
// Export parsed environment for direct access if needed
export { env };
//# sourceMappingURL=index.js.map