@etm-professional-control/winccoa-mcp-server
Version:
MCP Server for WinCC OA with field-specific configurations
367 lines (325 loc) • 14.3 kB
JavaScript
/**
* Icon Tools
*
* MCP tools for generating custom SVG icons for dashboard widgets
*/
import { z } from 'zod';
import { createSuccessResponse, createErrorResponse } from '../../utils/helpers.js';
import { IconGenerator } from '../../helpers/icons/IconGenerator.js';
import { IconList } from '../../helpers/icons/IconList.js';
/**
* Register icon tools with the MCP server
* @param server - MCP server instance
* @param context - Shared context
* @returns Number of tools registered
*/
export function registerTools(server, context) {
const iconGenerator = new IconGenerator();
const iconList = new IconList();
// ==================== CREATE CUSTOM ICON ====================
server.tool('create-custom-icon', `Create a custom SVG icon for dashboard widgets.
Icons are saved to /data/WebUI/icons/ and can be referenced in widget headers/footers.
After creating an icon, use its path in the titleIcon or subtitleIcon parameter when editing widgets.
IMPORTANT: Icons must be small (24x24 pixels by default) to match Siemens IX icon library.
Widget header/footer icons cannot be full-width - they must be icon-sized.
WHEN TO CREATE CUSTOM ICONS:
- ONLY if list-ix-icons search returns no suitable match from the 1,407 IX icons
- For brand-specific symbols not in the standard Siemens library
- For domain-specific industrial icons unique to your application
---
## SIEMENS IX DESIGN GUIDELINES
When creating custom SVG icons, follow Siemens Industrial Experience standards to ensure visual consistency:
**Grid & Size:**
- Base grid: 24×24 pixels (matches IX icon library exactly)
- Include 2px clearance zone from edges (icons should not touch outer boundary)
- Use geometric shapes with sharp corners and straight lines
**Stroke & Style:**
- Default stroke width: 2px (preferred for all icons)
- Use 1.5px or 1px stroke ONLY when readability requires it
- Prefer outlines over filled shapes (use fills sparingly)
- Maintain 2px gaps between separate shapes to prevent anti-aliasing blur
**Design Principles:**
- Keep it simple and geometric - avoid complex details that don't scale well
- Use straight lines and sharp corners as default (NOT rounded)
- Only use curves when representing actual object characteristics (e.g., gauge needles, circles)
- Avoid unsafe pixel patterns that create visual ambiguity
**Technical Requirements:**
- All strokes must be converted to outlines before export
- Combine shapes into single path via boolean operations where possible
- Size: 24×24px for widget headers/footers (DO NOT use larger sizes)
- Format: SVG with currentColor support for theme compatibility
**Color Usage:**
- Use "currentColor" as default (adapts to light/dark themes automatically)
- Only specify custom colors for brand-specific or status-indicating icons
- Avoid hardcoded colors unless absolutely necessary
**Reference:** https://ix.siemens.io/docs/icons/design-new-icons
---
Parameters:
- name: Icon filename (without .svg extension) (required)
- type: Icon type - "simple", "trend", "gauge", "alert", or "custom" (required)
- color: SVG color (optional, default: "currentColor" for theme support)
- size: Viewbox size in pixels (optional, default: 24, supported: 16, 24, 32)
- customSvg: Custom SVG path data (required only for "custom" type)
Icon Types:
- simple: Basic circle icon (24x24)
- trend: Line chart/trend icon with data points (24x24)
- gauge: Semicircular gauge/meter icon (24x24)
- alert: Warning triangle icon (24x24)
- custom: Custom shape from SVG path data (size configurable, default 24x24)
Returns: Path to the created icon (e.g., "/data/WebUI/icons/my-icon.svg")
Example 1 - Create a trend icon:
{
"name": "production-trend",
"type": "trend",
"color": "#4ecdc4"
}
→ Result: /data/WebUI/icons/production-trend.svg
Example 2 - Create a gauge icon:
{
"name": "temperature-gauge",
"type": "gauge",
"color": "#ff6b6b",
"size": 24
}
→ Result: /data/WebUI/icons/temperature-gauge.svg
Example 3 - Create an alert icon:
{
"name": "critical-alert",
"type": "alert",
"color": "#f38181"
}
→ Result: /data/WebUI/icons/critical-alert.svg
Example 4 - Create a custom icon:
{
"name": "my-custom-icon",
"type": "custom",
"customSvg": "M12 2 L22 12 L12 22 L2 12 Z",
"color": "#95e1d3",
"size": 24
}
→ Result: /data/WebUI/icons/my-custom-icon.svg
---
ALL icon types support custom colors via the "color" parameter!
**Status-Indicating Colors:**
- Red alerts: {"name": "high-alarm", "type": "alert", "color": "#ff0000"}
- Orange warnings: {"name": "medium-warning", "type": "alert", "color": "#ff6b35"}
- Green success: {"name": "ok-status", "type": "custom", "customSvg": "M4 12 L10 18 L20 4", "color": "#00ff00"}
- Blue info: {"name": "info-indicator", "type": "trend", "color": "#4ecdc4"}
**Brand-Specific Colors:**
- Company brand: {"name": "brand-logo", "type": "custom", "customSvg": "...", "color": "#YOUR_BRAND_COLOR"}
- Department colors: {"name": "production-icon", "type": "gauge", "color": "#ffa500"}
**Theme-Aware (Default):**
- Use "currentColor" or omit color parameter entirely for icons that adapt to light/dark themes
- Recommended for most icons unless you need specific status colors
**Common Industrial Colors:**
- Critical (Red):
- Warning (Orange/Yellow):
- OK/Running (Green):
- Info (Blue):
- Neutral (Gray):
---
After creating the icon, use it in a widget header or footer:
{
"dashboardId": "_Dashboard_000001",
"widgetIdentifier": {"index": 0},
"titleIcon": "/data/WebUI/icons/production-trend.svg",
"headerTitle": "Production Line 1"
}`, {
name: z.string().min(1, 'Icon name is required'),
type: z.enum(['simple', 'trend', 'gauge', 'alert', 'custom']),
color: z.string().optional(),
size: z.number().int().min(16).max(32).optional(),
customSvg: z.string().optional()
}, async (params) => {
try {
const { name, type, color, size, customSvg } = params;
console.log(`Creating custom icon: ${name} (type: ${type})`);
// Validate custom type requires customSvg
if (type === 'custom' && !customSvg) {
return createErrorResponse('Custom icon type requires customSvg parameter');
}
const iconPath = iconGenerator.generateIcon({
name,
type,
color,
size,
customSvg
});
return createSuccessResponse({
success: true,
iconPath,
message: `Icon created: ${iconPath}`,
usage: `Use in widget: { "titleIcon": "${iconPath}", "headerTitle": "Your Title" }`
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error('Error creating icon:', error);
return createErrorResponse(`Failed to create icon: ${errorMessage}`);
}
});
// ==================== LIST CUSTOM ICONS ====================
server.tool('list-custom-icons', `List all available custom icons in /data/WebUI/icons/.
Returns an array of icon paths that can be used in widget headers/footers.
Example usage:
Call this tool to see all available custom icons, then use the path in edit-widget:
{
"dashboardId": "_Dashboard_000001",
"widgetIdentifier": {"index": 0},
"titleIcon": "/data/WebUI/icons/your-icon.svg"
}`, {}, async () => {
try {
console.log('Listing custom icons');
const icons = iconGenerator.listCustomIcons();
return createSuccessResponse({
success: true,
count: icons.length,
icons,
message: icons.length > 0
? `Found ${icons.length} custom icon(s)`
: 'No custom icons found. Create one with create-custom-icon tool.'
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error('Error listing icons:', error);
return createErrorResponse(`Failed to list icons: ${errorMessage}`);
}
});
// ==================== DELETE CUSTOM ICON ====================
server.tool('delete-custom-icon', `Delete a custom icon from /data/WebUI/icons/.
Parameters:
- name: Icon filename (with or without .svg extension) (required)
Example:
{
"name": "my-icon.svg"
}`, {
name: z.string().min(1, 'Icon name is required')
}, async (params) => {
try {
const { name } = params;
console.log(`Deleting custom icon: ${name}`);
const deleted = iconGenerator.deleteIcon(name);
if (deleted) {
return createSuccessResponse({
success: true,
message: `Icon deleted: ${name}`
});
}
else {
return createErrorResponse(`Icon not found: ${name}`);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error('Error deleting icon:', error);
return createErrorResponse(`Failed to delete icon: ${errorMessage}`);
}
});
// ==================== LIST SIEMENS IX ICONS ====================
server.tool('list-ix-icons', `Search and filter through 1,407 built-in Siemens IX icons.
This tool helps you find the correct icon names from the Siemens Industrial Experience (IX) icon library.
All icons are 24×24 pixels and can be used directly in widget titleIcon and subtitleIcon parameters.
Parameters:
- search: Optional keyword to filter icons (e.g., "trend", "chart", "warning") (optional)
- category: Optional category filter - one of: trend, chart, status, action, navigation, settings, time, device, user, file, plant, battery, network (optional)
- limit: Maximum number of results to return (default: 50, max: 200) (optional)
Icon Categories:
- trend: Trend indicators (trend-upward, trend-downward, trend-sideways, etc.)
- chart: Chart types (barchart, linechart, piechart, areachart, gaugechart, etc.)
- status: Status indicators (alarm, error, warning, success, info, etc.)
- action: User actions (pen, trashcan, download, upload, save, refresh, etc.)
- navigation: Navigation (home, search, chevron-*, arrow-*, etc.)
- settings: Configuration (cogwheel, settings, configuration, database, etc.)
- time: Time-related (clock, calendar, hourglass, stopwatch, etc.)
- device: Hardware (generic-device, plc-device, network-device, sensor, etc.)
- user: People/users (user, user-profile, user-group, add-user, etc.)
- file: Files/documents (document, folder, pdf-document, json-document, etc.)
- plant: Industrial plants (plant, building1, building2, create-plant, etc.)
- battery: Power status (battery-full, battery-low, battery-empty, etc.)
- network: Connectivity (connected, disconnected, wlan-strength*, etc.)
Example 1 - Search for trend icons:
{
"search": "trend"
}
→ Returns: trend, trend-upward, trend-downward, trend-sideways, etc.
Example 2 - Get chart category icons:
{
"category": "chart"
}
→ Returns: All chart icons (barchart, linechart, piechart, etc.)
Example 3 - Search within a category:
{
"category": "status",
"search": "warning",
"limit": 10
}
→ Returns: warning, warning-filled, warning-rhomb, etc.
Example 4 - Browse battery icons:
{
"category": "battery"
}
→ Returns: battery-full, battery-half, battery-low, battery-empty, etc.
Usage in widgets:
After finding an icon, use it directly in edit-widget:
{
"dashboardId": "_Dashboard_000001",
"widgetIdentifier": {"index": 0},
"titleIcon": "trend-upward",
"headerTitle": "Production Trend"
}
See IX_ICONS_REFERENCE.md for complete documentation and visual examples.`, {
search: z.string().optional(),
category: z.enum([
'trend', 'chart', 'status', 'action', 'navigation',
'settings', 'time', 'device', 'user', 'file',
'plant', 'battery', 'network'
]).optional(),
limit: z.number().int().min(1).max(200).optional()
}, async (params) => {
try {
const { search, category, limit = 50 } = params;
console.log(`Searching IX icons - category: ${category || 'all'}, search: ${search || 'none'}, limit: ${limit}`);
let icons;
if (category) {
// Search within category
icons = iconList.searchByCategory(category, search, limit);
}
else if (search) {
// Search all icons
icons = iconList.searchIcons(search, limit);
}
else {
// Return random sample or first N icons
icons = iconList.searchIcons('', limit);
}
const totalIcons = iconList.getTotalCount();
const allCategories = iconList.getAllCategories();
return createSuccessResponse({
success: true,
count: icons.length,
totalAvailable: totalIcons,
icons,
categories: allCategories.map(cat => ({
name: cat.name,
description: cat.description,
iconCount: cat.icons.length
})),
message: category
? `Found ${icons.length} icon(s) in category "${category}"`
: search
? `Found ${icons.length} icon(s) matching "${search}"`
: `Showing ${icons.length} of ${totalIcons} available icons`,
usage: 'Use icon names directly in titleIcon or subtitleIcon parameters'
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error('Error listing IX icons:', error);
return createErrorResponse(`Failed to list IX icons: ${errorMessage}`);
}
});
return 4; // Number of tools registered
}
//# sourceMappingURL=icon.js.map