Skip to main content
Glama
admin.ts12.6 kB
/** * @file Admin API Routes * @description API endpoints for admin dashboard - MCP tools settings and backend configurations. * * SECURITY NOTES (ATTT cấp 3): * - All routes require admin authentication * - All changes are logged with user ID and IP * - Secrets are never exposed in responses */ import { Router, Request, Response } from 'express'; import { mcpToolSettingsService } from '../mcp/settings/index.js'; import { McpToolSettingUpdateSchema, BackendConfigUpdateSchema } from '../mcp/settings/types.js'; import { logger } from '../logging/logger.js'; import { z } from 'zod'; /** * Create admin routes for MCP tools and backend configurations. */ export function createAdminRoutes(): Router { const router = Router(); // ========================================================================== // MCP Tools Settings Routes // ========================================================================== /** * GET /admin/mcp-tools * List all MCP tools with their current settings. */ router.get('/mcp-tools', async (req: Request, res: Response) => { try { const result = await mcpToolSettingsService.getAllToolSettings(); res.json(result); } catch (error) { logger.error('Failed to list MCP tools', { error }); res.status(500).json({ error: 'Failed to list MCP tools', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * GET /admin/mcp-tools/:toolName * Get settings for a specific tool. */ router.get('/mcp-tools/:toolName', async (req: Request, res: Response) => { try { const { toolName } = req.params; const setting = mcpToolSettingsService.getToolSetting(toolName); res.json({ toolName, setting, }); } catch (error) { logger.error('Failed to get tool setting', { error, toolName: req.params.toolName }); res.status(500).json({ error: 'Failed to get tool setting', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * PUT /admin/mcp-tools/:toolName * Update settings for a specific tool. */ router.put('/mcp-tools/:toolName', async (req: Request, res: Response) => { try { const { toolName } = req.params; // Validate input const parseResult = McpToolSettingUpdateSchema.safeParse(req.body); if (!parseResult.success) { res.status(400).json({ error: 'Invalid input', details: parseResult.error.errors, }); return; } // Get user info from auth (if available) const userId = (req as any).user?.id || 'anonymous'; const auditContext = { ipAddress: req.ip || req.socket.remoteAddress, userAgent: req.get('User-Agent'), }; const setting = await mcpToolSettingsService.updateToolSetting( toolName, parseResult.data, userId, auditContext ); logger.info('Tool setting updated via API', { toolName, userId }); res.json({ success: true, toolName, setting, }); } catch (error) { logger.error('Failed to update tool setting', { error, toolName: req.params.toolName }); res.status(500).json({ error: 'Failed to update tool setting', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * POST /admin/mcp-tools/:toolName/toggle * Quick toggle to enable/disable a tool. */ router.post('/mcp-tools/:toolName/toggle', async (req: Request, res: Response) => { try { const { toolName } = req.params; const { enabled } = req.body; if (typeof enabled !== 'boolean') { res.status(400).json({ error: 'Invalid input', message: '"enabled" must be a boolean', }); return; } const userId = (req as any).user?.id || 'anonymous'; const auditContext = { ipAddress: req.ip || req.socket.remoteAddress, userAgent: req.get('User-Agent'), }; const setting = await mcpToolSettingsService.updateToolSetting( toolName, { enabled }, userId, auditContext ); logger.info('Tool toggled via API', { toolName, enabled, userId }); res.json({ success: true, toolName, enabled: setting.enabled, }); } catch (error) { logger.error('Failed to toggle tool', { error, toolName: req.params.toolName }); res.status(500).json({ error: 'Failed to toggle tool', message: error instanceof Error ? error.message : 'Unknown error', }); } }); // ========================================================================== // Backend Configurations Routes // ========================================================================== /** * GET /admin/backends * List all backend configurations. */ router.get('/backends', async (req: Request, res: Response) => { try { const result = await mcpToolSettingsService.getAllBackendConfigs(); res.json(result); } catch (error) { logger.error('Failed to list backends', { error }); res.status(500).json({ error: 'Failed to list backends', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * GET /admin/backends/:id * Get a specific backend configuration. */ router.get('/backends/:id', async (req: Request, res: Response) => { try { const { id } = req.params; const config = mcpToolSettingsService.getBackendConfig(id); if (!config) { res.status(404).json({ error: 'Backend not found', id, }); return; } res.json({ id, config, }); } catch (error) { logger.error('Failed to get backend', { error, id: req.params.id }); res.status(500).json({ error: 'Failed to get backend', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * PUT /admin/backends/:id * Create or update a backend configuration. */ router.put('/backends/:id', async (req: Request, res: Response) => { try { const { id } = req.params; // Validate input const parseResult = BackendConfigUpdateSchema.safeParse(req.body); if (!parseResult.success) { res.status(400).json({ error: 'Invalid input', details: parseResult.error.errors, }); return; } const userId = (req as any).user?.id || 'anonymous'; const auditContext = { ipAddress: req.ip || req.socket.remoteAddress, userAgent: req.get('User-Agent'), }; const config = await mcpToolSettingsService.upsertBackendConfig( id, parseResult.data, userId, auditContext ); logger.info('Backend config updated via API', { id, userId }); res.json({ success: true, id, config, }); } catch (error) { logger.error('Failed to update backend', { error, id: req.params.id }); res.status(500).json({ error: 'Failed to update backend', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * DELETE /admin/backends/:id * Delete a backend configuration. */ router.delete('/backends/:id', async (req: Request, res: Response) => { try { const { id } = req.params; const userId = (req as any).user?.id || 'anonymous'; const auditContext = { ipAddress: req.ip || req.socket.remoteAddress, userAgent: req.get('User-Agent'), }; const deleted = await mcpToolSettingsService.deleteBackendConfig( id, userId, auditContext ); if (!deleted) { res.status(404).json({ error: 'Backend not found', id, }); return; } logger.info('Backend config deleted via API', { id, userId }); res.json({ success: true, id, }); } catch (error) { logger.error('Failed to delete backend', { error, id: req.params.id }); res.status(500).json({ error: 'Failed to delete backend', message: error instanceof Error ? error.message : 'Unknown error', }); } }); /** * POST /admin/backends/:id/toggle * Quick toggle to enable/disable a backend. */ router.post('/backends/:id/toggle', async (req: Request, res: Response) => { try { const { id } = req.params; const { enabled } = req.body; if (typeof enabled !== 'boolean') { res.status(400).json({ error: 'Invalid input', message: '"enabled" must be a boolean', }); return; } const userId = (req as any).user?.id || 'anonymous'; const auditContext = { ipAddress: req.ip || req.socket.remoteAddress, userAgent: req.get('User-Agent'), }; const config = await mcpToolSettingsService.upsertBackendConfig( id, { enabled }, userId, auditContext ); logger.info('Backend toggled via API', { id, enabled, userId }); res.json({ success: true, id, enabled: config.enabled, }); } catch (error) { logger.error('Failed to toggle backend', { error, id: req.params.id }); res.status(500).json({ error: 'Failed to toggle backend', message: error instanceof Error ? error.message : 'Unknown error', }); } }); // ========================================================================== // Audit Log Routes // ========================================================================== /** * GET /admin/audit-logs * Get audit logs for settings changes. */ router.get('/audit-logs', async (req: Request, res: Response) => { try { const { entityType, entityId, userId, since, limit } = req.query; const logs = await mcpToolSettingsService.getAuditLogs({ entityType: entityType as 'tool_setting' | 'backend_config' | undefined, entityId: entityId as string | undefined, userId: userId as string | undefined, since: since as string | undefined, limit: limit ? parseInt(limit as string) : 100, }); res.json({ logs, count: logs.length, }); } catch (error) { logger.error('Failed to get audit logs', { error }); res.status(500).json({ error: 'Failed to get audit logs', message: error instanceof Error ? error.message : 'Unknown error', }); } }); return router; } export default createAdminRoutes;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/babasida246/ai-mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server