Skip to main content
Glama
Raistlin82

SAP OData to MCP Server

by Raistlin82

ui-dashboard-composer

Create comprehensive KPI dashboards with charts and real-time data from SAP systems to visualize business metrics and monitor performance.

Instructions

Creates comprehensive KPI dashboards with charts and real-time data

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dashboardTitleYesTitle for the dashboard
widgetsYesDashboard widget configurations
layoutNoDashboard layout style

Implementation Reference

  • Registers the 'ui-dashboard-composer' tool with the MCP server, specifying title, detailed description, input schema, and the handler function that delegates to handleDashboardComposition.
        public async register(): Promise<void> {
            this.mcpServer.registerTool(
                "ui-dashboard-composer",
                {
                    title: "UI Dashboard Composer",
                    description: `Create interactive KPI dashboards with widgets, charts, and real-time updates.
    
    Features:
    - Multiple widget types (KPI cards, charts, tables, gauges, timelines)
    - Flexible grid-based layout system
    - Real-time data refresh and updates
    - Interactive filters and drill-down capabilities
    - Chart.js integration for advanced visualizations
    - Export to PDF, Excel, PowerPoint, and images
    - Responsive design for mobile and desktop
    - SAP Fiori design language compliance
    - Custom aggregations and calculations
    - WebSocket support for live data
    
    Required scope: ui.dashboards
    
    Widget Types:
    - kpi-card: Key performance indicator cards
    - chart: Bar, line, pie, doughnut, and radar charts
    - table: Data tables with sorting and filtering
    - list: Simple list displays
    - gauge: Circular and linear progress gauges
    - timeline: Event timelines and process flows
    - map: Geographic data visualization
    - custom: Custom HTML/JavaScript widgets
    
    Examples:
    - Sales dashboard: {"title": "Sales Overview", "widgets": [...]}
    - Executive summary: {"title": "Executive KPIs", "layout": {"type": "grid", "columns": 3}}
    - Real-time monitoring: {"refreshInterval": 30, "widgets": [...]}`,
                    inputSchema: UIDashboardComposerSchema
                },
                async (args: Record<string, unknown>) => {
                    return await this.handleDashboardComposition(args);
                }
            );
    
            this.logger.info("βœ… UI Dashboard Composer tool registered successfully");
        }
  • Comprehensive Zod input schema defining all parameters for dashboard configuration including layout, widgets, data sources, filters, theme, and export options.
    const UIDashboardComposerSchema = {
        title: z.string().describe("Dashboard title"),
        description: z.string().optional().describe("Dashboard description"),
        layout: z.object({
            type: z.enum(['grid', 'flexbox', 'absolute']).describe("Layout type"),
            columns: z.number().min(1).max(12).optional().describe("Number of columns for grid layout"),
            gap: z.string().optional().describe("Gap between widgets (e.g., '1rem')"),
            responsive: z.boolean().optional().describe("Enable responsive design")
        }).describe("Dashboard layout configuration"),
        widgets: z.array(z.object({
            id: z.string().describe("Unique widget identifier"),
            type: z.enum(['kpi-card', 'chart', 'table', 'list', 'gauge', 'timeline', 'map', 'custom']).describe("Widget type"),
            title: z.string().describe("Widget title"),
            position: z.object({
                row: z.number().min(0).describe("Grid row position"),
                col: z.number().min(0).describe("Grid column position"),
                width: z.number().min(1).max(12).describe("Widget width (grid columns)"),
                height: z.number().min(1).describe("Widget height (grid rows)")
            }).describe("Widget position and size"),
            config: z.record(z.any()).optional().describe("Widget-specific configuration"),
            dataSource: z.object({
                entitySet: z.string().describe("SAP entity set for data"),
                query: z.object({
                    filter: z.string().optional(),
                    select: z.string().optional(),
                    orderby: z.string().optional(),
                    top: z.number().optional()
                }).optional().describe("OData query parameters"),
                aggregation: z.object({
                    groupBy: z.array(z.string()).optional().describe("Fields to group by"),
                    measures: z.array(z.object({
                        field: z.string(),
                        operation: z.enum(['sum', 'avg', 'count', 'min', 'max'])
                    })).optional().describe("Aggregation measures")
                }).optional().describe("Data aggregation configuration"),
                refresh: z.number().optional().describe("Refresh interval in seconds")
            }).describe("Data source configuration")
        })).describe("Dashboard widgets"),
        datasources: z.array(z.object({
            id: z.string().describe("Data source identifier"),
            entitySet: z.string().describe("SAP entity set"),
            query: z.string().optional().describe("Custom OData query"),
            cacheTtl: z.number().optional().describe("Cache TTL in seconds"),
            transform: z.string().optional().describe("Data transformation function name")
        })).optional().describe("Shared data sources"),
        refreshInterval: z.number().min(5).max(3600).optional().describe("Global refresh interval in seconds"),
        theme: z.enum(['sap_horizon', 'sap_fiori_3', 'dark', 'light']).optional().describe("Dashboard theme"),
        filters: z.array(z.object({
            field: z.string().describe("Filter field name"),
            label: z.string().describe("Filter display label"),
            type: z.enum(['select', 'daterange', 'text', 'number']).describe("Filter type"),
            options: z.array(z.object({
                value: z.string(),
                label: z.string()
            })).optional().describe("Options for select filters"),
            defaultValue: z.any().optional().describe("Default filter value")
        })).optional().describe("Global dashboard filters"),
        exportOptions: z.object({
            pdf: z.boolean().optional(),
            excel: z.boolean().optional(),
            powerpoint: z.boolean().optional(),
            image: z.boolean().optional()
        }).optional().describe("Export format options")
    };
  • Core handler function that parses input, checks authorization, validates and prepares widgets/data, generates dashboard layout and UI using component library, enhances with SAP styles/scripts, and returns markdown summary with JSON resource containing full dashboard config.
    private async handleDashboardComposition(args: unknown): Promise<any> {
        try {
            // Validate input parameters
            const params = z.object(UIDashboardComposerSchema).parse(args);
    
            this.logger.info(`πŸ“Š Generating dashboard: ${params.title} with ${params.widgets.length} widgets`);
    
            // Check authentication and authorization
            const authCheck = await this.checkUIAccess('ui.dashboards');
            if (!authCheck.hasAccess) {
                return {
                    content: [{
                        type: "text",
                        text: `❌ Authorization denied: ${authCheck.reason || 'Access denied for UI dashboard generation'}\n\nRequired scope: ui.dashboards`
                    }]
                };
            }
    
            // Step 1: Validate and prepare widget configurations
            const validatedWidgets = await this.validateAndPrepareWidgets(params.widgets);
    
            // Step 2: Prepare data sources
            const dataSources = await this.prepareDashboardDataSources(validatedWidgets, params.datasources);
    
            // Step 3: Create dashboard layout
            const layoutDefinition: LayoutDefinition = {
                type: params.layout.type,
                config: {
                    columns: params.layout.columns || 4,
                    gap: params.layout.gap || '1rem',
                    responsive: params.layout.responsive !== false ? {
                        breakpoints: {
                            mobile: { columns: 1, gap: '0.5rem' },
                            tablet: { columns: 2, gap: '1rem' },
                            desktop: { columns: 4, gap: '1rem' }
                        }
                    } : undefined
                },
                components: this.createWidgetComponents(validatedWidgets)
            };
    
            // Step 4: Create dashboard configuration
            const dashboardConfig: DashboardConfig = {
                layout: layoutDefinition,
                widgets: validatedWidgets,
                datasources: dataSources,
                refreshInterval: params.refreshInterval || 60,
                theme: params.theme || 'sap_horizon'
            };
    
            // Step 5: Generate dashboard UI
            const dashboardResult = await this.componentLibrary.generateDashboard(dashboardConfig);
    
            // Step 6: Add SAP-specific enhancements
            const enhancedResult = await this.enhanceDashboardResult(dashboardResult, params);
    
            // Step 7: Prepare response
            const response = this.createDashboardResponse(enhancedResult, params, dataSources);
    
            this.logger.info(`βœ… Dashboard '${params.title}' generated successfully`);
    
            return {
                content: [
                    {
                        type: "text",
                        text: `# ${params.title}\n\n` +
                              `${params.description || 'Interactive SAP dashboard with real-time KPIs'}\n\n` +
                              `## Dashboard Overview:\n` +
                              `- Widgets: ${params.widgets.length}\n` +
                              `- Layout: ${params.layout.type} (${params.layout.columns || 4} columns)\n` +
                              `- Theme: ${params.theme || 'sap_horizon'}\n` +
                              `- Refresh: Every ${params.refreshInterval || 60} seconds\n` +
                              `- Filters: ${params.filters?.length || 0}\n\n` +
                              `## Widget Types:\n` +
                              params.widgets.map(w => `- ${w.type}: ${w.title}`).join('\n') + '\n\n' +
                              `## Data Sources:\n` +
                              `- Entity Sets: ${Array.from(new Set(params.widgets.map(w => w.dataSource.entitySet))).join(', ')}\n` +
                              `- Real-time Updates: ${params.refreshInterval ? 'βœ…' : '❌'}\n\n` +
                              `## Features:\n` +
                              `- Export Options: ${Object.entries(params.exportOptions || {}).filter(([k, v]) => v).map(([k]) => k.toUpperCase()).join(', ') || 'Basic'}\n` +
                              `- Responsive Design: βœ…\n` +
                              `- Interactive Filters: ${params.filters?.length ? 'βœ…' : '❌'}\n\n` +
                              `Embed this dashboard in your SAP application or use via MCP client.`
                    },
                    {
                        type: "resource",
                        data: response,
                        mimeType: "application/json"
                    }
                ]
            };
    
        } catch (error) {
            this.logger.error(`❌ Failed to generate dashboard`, error as Error);
            return {
                content: [{
                    type: "text",
                    text: `❌ Failed to generate dashboard: ${(error as Error).message}`
                }]
            };
        }
    }

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/Raistlin82/btp-sap-odata-to-mcp-server-optimized'

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