analyze_icecast_config
Check an Icecast XML configuration file for security flaws, performance issues, capacity limits, and reliability risks, then receive actionable recommendations to optimize your streaming server.
Instructions
Analyze an Icecast XML configuration file and provide recommendations for improvements. Checks security, performance, capacity, and reliability settings.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| configPath | Yes | Path to the Icecast XML configuration file | |
| expectedListeners | No | Expected number of concurrent listeners (optional, default: 100) |
Implementation Reference
- src/index.ts:319-357 (registration)Tool registration in ListToolsRequestSchema handler under the name 'analyze_icecast_config' with input schema requiring configPath and optional expectedListeners.
server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "analyze_icecast_config", description: "Analyze an Icecast XML configuration file and provide recommendations for improvements. Checks security, performance, capacity, and reliability settings.", inputSchema: { type: "object", properties: { configPath: { type: "string", description: "Path to the Icecast XML configuration file", }, expectedListeners: { type: "number", description: "Expected number of concurrent listeners (optional, default: 100)", }, }, required: ["configPath"], }, }, { name: "get_icecast_best_practices", description: "Get general best practices and recommendations for Icecast configuration based on use case", inputSchema: { type: "object", properties: { useCase: { type: "string", description: "Use case: 'small' (< 50 listeners), 'medium' (50-500 listeners), 'large' (500+ listeners)", enum: ["small", "medium", "large"], }, }, required: ["useCase"], }, }, ], }; }); - src/index.ts:369-398 (handler)Main handler logic in CallToolRequestSchema. Reads the XML file from disk, parses it, analyzes the config, and returns a formatted report.
if (name === "analyze_icecast_config") { const configPath = args.configPath as string; const expectedListeners = (args.expectedListeners as number) || 100; try { const xmlContent = readFileSync(configPath, "utf-8"); const config = parseIcecastConfig(xmlContent); const issues = analyzeConfig(config, { expectedListeners }); const report = formatIssues(issues); return { content: [ { type: "text", text: `# Icecast Configuration Analysis\n\nAnalyzing: ${configPath}\nExpected listeners: ${expectedListeners}\n\n${report}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error analyzing configuration: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } - src/index.ts:14-67 (schema)Zod schema defining the shape of an Icecast configuration object, used for type inference and validation.
const IcecastConfigSchema = z.object({ location: z.string().optional(), admin: z.string().optional(), limits: z.object({ clients: z.number().optional(), sources: z.number().optional(), "queue-size": z.number().optional(), "burst-size": z.number().optional(), threadpool: z.number().optional(), "source-timeout": z.number().optional(), "header-timeout": z.number().optional(), "client-timeout": z.number().optional(), }).optional(), authentication: z.object({ "source-password": z.string().optional(), "admin-user": z.string().optional(), "admin-password": z.string().optional(), "relay-password": z.string().optional(), }).optional(), hostname: z.string().optional(), "listen-socket": z.union([ z.object({ port: z.number().optional(), "bind-address": z.string().optional(), }), z.array(z.object({ port: z.number().optional(), "bind-address": z.string().optional(), })) ]).optional(), fileserve: z.number().optional(), "use-x-forwarded-for": z.number().optional(), mount: z.union([ z.object({ "mount-name": z.string(), }), z.array(z.object({ "mount-name": z.string(), })) ]).optional(), paths: z.object({ logdir: z.string().optional(), webroot: z.string().optional(), adminroot: z.string().optional(), pidfile: z.string().optional(), }).optional(), logging: z.object({ accesslog: z.string().optional(), errorlog: z.string().optional(), loglevel: z.number().optional(), logsize: z.number().optional(), logarchive: z.number().optional(), }).optional(), }); - src/index.ts:81-90 (helper)Parses raw XML content into a typed IcecastConfig object using fast-xml-parser.
function parseIcecastConfig(xmlContent: string): IcecastConfig { const parser = new XMLParser({ ignoreAttributes: false, parseTagValue: true, parseAttributeValue: true, }); const parsed = parser.parse(xmlContent); return parsed.icecast || {}; } - src/index.ts:93-241 (helper)Core analysis function that inspects the parsed config and returns an array of ConfigIssue objects covering security, performance, capacity, and reliability.
function analyzeConfig(config: IcecastConfig, context?: { expectedListeners?: number }): ConfigIssue[] { const issues: ConfigIssue[] = []; const expectedListeners = context?.expectedListeners || 100; // Check client limits const clientLimit = config.limits?.clients; if (clientLimit !== undefined) { if (clientLimit > 1000 && expectedListeners < 500) { issues.push({ severity: "info", category: "Performance", issue: "Client limit may be unnecessarily high", recommendation: `Client limit is ${clientLimit}. Consider lowering to ${Math.max(128, expectedListeners * 2)} unless you expect high traffic.`, currentValue: clientLimit, recommendedValue: Math.max(128, expectedListeners * 2), }); } if (clientLimit < 50) { issues.push({ severity: "warning", category: "Capacity", issue: "Client limit is quite low", recommendation: `Client limit is ${clientLimit}. This may cause connection rejections during peak times.`, currentValue: clientLimit, recommendedValue: 128, }); } } else { issues.push({ severity: "warning", category: "Configuration", issue: "No client limit specified", recommendation: "Set an explicit client limit to control resource usage.", recommendedValue: 128, }); } // Check authentication if (!config.authentication) { issues.push({ severity: "critical", category: "Security", issue: "No authentication configured", recommendation: "Configure source-password and admin-password to secure your stream.", }); } else { if (config.authentication["admin-user"] === "admin") { issues.push({ severity: "warning", category: "Security", issue: "Using default admin username", recommendation: "Change admin username from 'admin' to something less predictable.", currentValue: "admin", }); } if (!config.authentication["relay-password"]) { issues.push({ severity: "info", category: "Security", issue: "No relay password configured", recommendation: "If you plan to use relays, configure a relay-password.", }); } } // Check mount configuration if (!config.mount) { issues.push({ severity: "warning", category: "Configuration", issue: "No mount points configured", recommendation: "Configure at least one mount point with appropriate settings.", }); } else { const mounts = Array.isArray(config.mount) ? config.mount : [config.mount]; if (mounts.length === 1) { issues.push({ severity: "info", category: "Reliability", issue: "No fallback mount configured", recommendation: "Consider adding a fallback mount for better reliability.", }); } } // Check logging if (config.logging) { if (!config.logging.logarchive) { issues.push({ severity: "info", category: "Maintenance", issue: "Log archiving not configured", recommendation: "Enable log archiving to automatically rotate old logs.", recommendedValue: 1, }); } const logLevel = config.logging.loglevel; if (logLevel !== undefined && logLevel > 3) { issues.push({ severity: "info", category: "Performance", issue: "High log verbosity", recommendation: `Log level is ${logLevel}. Consider level 3 for production (4 for debug).`, currentValue: logLevel, recommendedValue: 3, }); } } // Check burst size const burstSize = config.limits?.["burst-size"]; const queueSize = config.limits?.["queue-size"]; if (burstSize && queueSize && burstSize > queueSize / 2) { issues.push({ severity: "warning", category: "Performance", issue: "Burst size is very large relative to queue size", recommendation: `Burst size (${burstSize}) should typically be less than half of queue size (${queueSize}).`, currentValue: burstSize, recommendedValue: Math.floor(queueSize / 2), }); } // Check hostname if (config.hostname === "localhost") { issues.push({ severity: "info", category: "Configuration", issue: "Hostname is set to localhost", recommendation: "Set hostname to your actual domain name for proper stream URLs in directory listings.", }); } // Check X-Forwarded-For if (config["use-x-forwarded-for"] === 1) { issues.push({ severity: "info", category: "Configuration", issue: "X-Forwarded-For is enabled", recommendation: "Good! This is correct when running behind a reverse proxy like Caddy.", }); } return issues; }