Skip to main content
Glama

Robot Framework MCP Server

by sourcefuse
mcp_server.py44.1 kB
import re from string import Template from urllib.parse import urlparse from mcp.server.fastmcp import FastMCP # Create an enhanced MCP server with validation and configurable selectors mcp = FastMCP("Robot framework MCP Server") class ValidationError(Exception): """Custom exception for validation errors""" pass class InputValidator: """Centralized input validation for MCP tools""" @staticmethod def validate_url(url: str) -> str: """Validate and return sanitized URL""" if not url or not url.strip(): raise ValidationError("URL cannot be empty") url = url.strip() try: result = urlparse(url) if not all([result.scheme, result.netloc]): raise ValidationError(f"Invalid URL format: {url}") if result.scheme not in ['http', 'https']: raise ValidationError(f"URL must use http or https protocol: {url}") return url except Exception as e: raise ValidationError(f"URL validation failed: {str(e)}") @staticmethod def validate_credentials(username: str, password: str) -> tuple: """Validate and return sanitized credentials""" if not username or not username.strip(): raise ValidationError("Username cannot be empty") if not password or not password.strip(): raise ValidationError("Password cannot be empty") username = username.strip() password = password.strip() if len(username) > 100: raise ValidationError("Username too long (max 100 characters)") if len(password) > 100: raise ValidationError("Password too long (max 100 characters)") # Check for dangerous characters dangerous_chars = ['<', '>', '"', "'", '&', '\n', '\r', '\t'] for char in dangerous_chars: if char in username or char in password: raise ValidationError(f"Credentials contain invalid character: {char}") return username, password @staticmethod def validate_selector(selector: str) -> str: """Validate and return sanitized selector""" if not selector or not selector.strip(): raise ValidationError("Selector cannot be empty") selector = selector.strip() # Basic validation patterns valid_patterns = [ r'^id=.+', # id=element-id r'^name=.+', # name=element-name r'^class=.+', # class=element-class r'^css=.+', # css=.class-name r'^xpath=.+', # xpath=//div[@id='test'] r'^tag=.+', # tag=input r'^\w+', # plain CSS selector ] if not any(re.match(pattern, selector) for pattern in valid_patterns): raise ValidationError(f"Invalid selector format: {selector}") return selector # Predefined selector configurations for different applications SELECTOR_CONFIGS = { "appLocator": { "username_field": "id=user-name", "password_field": "id=password", "login_button": "id=login-button", "success_indicator": "xpath=//span[@class='title']", "error_message": "xpath=//h3[@data-test='error']", "logout_button": "id=logout_sidebar_link" }, "generic": { "username_field": "id=username", "password_field": "id=password", "login_button": "css=button[type='submit']", "success_indicator": "css=.dashboard", "error_message": "css=.error", "logout_button": "css=.logout" }, "bootstrap": { "username_field": "css=input[name='username']", "password_field": "css=input[name='password']", "login_button": "css=.btn-primary", "success_indicator": "css=.navbar-brand", "error_message": "css=.alert-danger", "logout_button": "css=.btn-outline-secondary" } } @mcp.tool() def create_login_test_case(url: str, username: str, password: str, template_type: str = "appLocator") -> str: """Generate Robot Framework test case code for login functionality. Returns the complete .robot file content as text - does not execute the test.""" try: # Validate inputs validated_url = InputValidator.validate_url(url) validated_username, validated_password = InputValidator.validate_credentials(username, password) # Get selector configuration selectors = SELECTOR_CONFIGS.get(template_type.lower(), SELECTOR_CONFIGS["generic"]) # Use Template for safe variable substitution and correct Robot Framework syntax template = Template("""*** Settings *** Library SeleniumLibrary *** Variables *** $${URL} $url $${USERNAME} $username $${PASSWORD} $password $${BROWSER} Chrome # Selector Variables $${USERNAME_FIELD} $username_field $${PASSWORD_FIELD} $password_field $${LOGIN_BUTTON} $login_button $${SUCCESS_INDICATOR} $success_indicator *** Test Cases *** Login Test [Documentation] Test login functionality for $template_type [Tags] smoke login $template_type Open Browser $${URL} $${BROWSER} Maximize Browser Window Input Text $${USERNAME_FIELD} $${USERNAME} Input Text $${PASSWORD_FIELD} $${PASSWORD} Click Button $${LOGIN_BUTTON} Wait Until Page Contains Element $${SUCCESS_INDICATOR} 10s Element Text Should Be $${SUCCESS_INDICATOR} Products [Teardown] Close Browser """) return template.substitute( url=validated_url, username=validated_username, password=validated_password, template_type=template_type, username_field=selectors["username_field"], password_field=selectors["password_field"], login_button=selectors["login_button"], success_indicator=selectors["success_indicator"] ) except ValidationError as e: return f"# VALIDATION ERROR: {str(e)}\n# Please correct the input and try again." except Exception as e: return f"# UNEXPECTED ERROR: {str(e)}\n# Please contact support." @mcp.tool() def create_page_object_login(template_type: str = "appLocator") -> str: """Generate Robot Framework page object model code for login page. Returns .robot file content as text - does not execute.""" try: # Get selector configuration selectors = SELECTOR_CONFIGS.get(template_type.lower(), SELECTOR_CONFIGS["generic"]) # Use Template for safe variable substitution and correct Robot Framework syntax template = Template("""*** Settings *** Library SeleniumLibrary *** Variables *** # $template_type Application Selectors $${LOGIN_USERNAME_FIELD} $username_field $${LOGIN_PASSWORD_FIELD} $password_field $${LOGIN_BUTTON} $login_button $${LOGIN_ERROR_MESSAGE} $error_message *** Keywords *** Input Username [Arguments] $${username} [Documentation] Enter username in the username field Wait Until Element Is Visible $${LOGIN_USERNAME_FIELD} Clear Element Text $${LOGIN_USERNAME_FIELD} Input Text $${LOGIN_USERNAME_FIELD} $${username} Input Password [Arguments] $${password} [Documentation] Enter password in the password field Wait Until Element Is Visible $${LOGIN_PASSWORD_FIELD} Clear Element Text $${LOGIN_PASSWORD_FIELD} Input Text $${LOGIN_PASSWORD_FIELD} $${password} Click Login Button [Documentation] Click the login button Wait Until Element Is Enabled $${LOGIN_BUTTON} Click Button $${LOGIN_BUTTON} Login With Credentials [Arguments] $${username} $${password} [Documentation] Complete login process with given credentials Input Username $${username} Input Password $${password} Click Login Button Verify Error Message [Arguments] $${expected_message} [Documentation] Verify error message is displayed Wait Until Element Is Visible $${LOGIN_ERROR_MESSAGE} 10s Element Text Should Be $${LOGIN_ERROR_MESSAGE} $${expected_message} """) return template.substitute( template_type=template_type.upper(), username_field=selectors["username_field"], password_field=selectors["password_field"], login_button=selectors["login_button"], error_message=selectors["error_message"] ) except Exception as e: return f"# ERROR: {str(e)}\n# Please contact support." @mcp.tool() def create_advanced_selenium_keywords() -> str: """Generate Robot Framework keywords for advanced Selenium operations. Returns .robot file content as text - does not execute.""" template = """*** Settings *** Library SeleniumLibrary *** Keywords *** # Dropdown/Select Operations Select Dropdown Option By Label [Arguments] ${locator} ${label} [Documentation] Select option from dropdown by visible text Wait Until Element Is Visible ${locator} 10s Select From List By Label ${locator} ${label} Select Dropdown Option By Value [Arguments] ${locator} ${value} [Documentation] Select option from dropdown by value Wait Until Element Is Visible ${locator} 10s Select From List By Value ${locator} ${value} # Checkbox Operations Select Checkbox If Not Selected [Arguments] ${locator} [Documentation] Select checkbox only if it's not already selected Wait Until Element Is Visible ${locator} 10s ${is_selected}= Run Keyword And Return Status Checkbox Should Be Selected ${locator} Run Keyword If not ${is_selected} Select Checkbox ${locator} Unselect Checkbox If Selected [Arguments] ${locator} [Documentation] Unselect checkbox only if it's currently selected Wait Until Element Is Visible ${locator} 10s ${is_selected}= Run Keyword And Return Status Checkbox Should Be Selected ${locator} Run Keyword If ${is_selected} Unselect Checkbox ${locator} # File Upload Operations Upload File To Element [Arguments] ${locator} ${file_path} [Documentation] Upload file using file input element Wait Until Element Is Visible ${locator} 10s Choose File ${locator} ${file_path} # Alert/Pop-up Operations Handle Alert And Accept [Documentation] Handle JavaScript alert and accept it Alert Should Be Present Accept Alert Handle Alert And Dismiss [Documentation] Handle JavaScript alert and dismiss it Alert Should Be Present Dismiss Alert Get Alert Text And Accept [Documentation] Get alert text and accept the alert Alert Should Be Present ${alert_text}= Get Alert Message Accept Alert RETURN ${alert_text} # Mouse Operations Hover Over Element [Arguments] ${locator} [Documentation] Hover mouse over an element Wait Until Element Is Visible ${locator} 10s Mouse Over ${locator} Double Click On Element [Arguments] ${locator} [Documentation] Double click on an element Wait Until Element Is Visible ${locator} 10s Double Click Element ${locator} Right Click On Element [Arguments] ${locator} [Documentation] Right click on an element Wait Until Element Is Visible ${locator} 10s Open Context Menu ${locator} # Scroll Operations Scroll To Element [Arguments] ${locator} [Documentation] Scroll element into view Wait Until Element Is Visible ${locator} 10s Scroll Element Into View ${locator} Scroll To Bottom Of Page [Documentation] Scroll to the bottom of the page Execute JavaScript window.scrollTo(0, document.body.scrollHeight) Scroll To Top Of Page [Documentation] Scroll to the top of the page Execute JavaScript window.scrollTo(0, 0) # Window/Tab Operations Switch To New Window [Documentation] Switch to the newly opened window/tab ${current_windows}= Get Window Handles ${window_count}= Get Length ${current_windows} Should Be True ${window_count} > 1 New window should be opened Switch Window ${current_windows}[-1] Close Current Window And Switch Back [Documentation] Close current window and switch to previous one Close Window Switch Window MAIN # JavaScript Operations Execute Custom JavaScript [Arguments] ${javascript_code} [Documentation] Execute custom JavaScript code ${result}= Execute JavaScript ${javascript_code} RETURN ${result} Set Element Attribute [Arguments] ${locator} ${attribute} ${value} [Documentation] Set attribute value of an element using JavaScript Wait Until Element Is Visible ${locator} 10s Execute JavaScript arguments[0].setAttribute('${attribute}', '${value}'); ARGUMENTS ${locator} Get Element Attribute Value [Arguments] ${locator} ${attribute} [Documentation] Get attribute value of an element Wait Until Element Is Visible ${locator} 10s ${value}= Get Element Attribute ${locator} ${attribute} RETURN ${value} # Advanced Wait Operations Wait Until Element Contains Text [Arguments] ${locator} ${expected_text} ${timeout}=10s [Documentation] Wait until element contains specific text Wait Until Element Is Visible ${locator} ${timeout} Wait Until Element Contains ${locator} ${expected_text} ${timeout} Wait Until Page Title Contains [Arguments] ${expected_title} ${timeout}=10s [Documentation] Wait until page title contains expected text Wait Until Title Contains ${expected_title} ${timeout} Wait For Element To Disappear [Arguments] ${locator} ${timeout}=10s [Documentation] Wait for element to disappear from page Wait Until Element Is Not Visible ${locator} ${timeout} # Table Operations Get Table Cell Text [Arguments] ${table_locator} ${row} ${column} [Documentation] Get text from specific table cell ${cell_text}= Get Table Cell ${table_locator} ${row} ${column} RETURN ${cell_text} Get Table Row Count [Arguments] ${table_locator} [Documentation] Get number of rows in table ${row_count}= Get Element Count ${table_locator}//tr RETURN ${row_count} # Form Validation Verify Field Is Required [Arguments] ${locator} [Documentation] Verify field has required attribute ${is_required}= Get Element Attribute ${locator} required Should Not Be Empty ${is_required} Field should be required Verify Field Is Disabled [Arguments] ${locator} [Documentation] Verify field is disabled Element Should Be Disabled ${locator} Verify Field Is Enabled [Arguments] ${locator} [Documentation] Verify field is enabled Element Should Be Enabled ${locator} """ return template @mcp.tool() def create_extended_selenium_keywords() -> str: """Generate extended Robot Framework keywords for screenshots, performance monitoring, and window management. Returns .robot file content as text - does not execute.""" return """*** Settings *** Library SeleniumLibrary Library Collections Library String Library DateTime *** Keywords *** # Screenshot Capabilities Capture Full Page Screenshot [Arguments] ${filename}=page_screenshot.png [Documentation] Capture screenshot of entire page Capture Page Screenshot ${filename} Log Screenshot saved as: ${filename} Capture Element Screenshot [Arguments] ${locator} ${filename}=element_screenshot.png [Documentation] Capture screenshot of specific element Wait Until Element Is Visible ${locator} 10s Capture Element Screenshot ${locator} ${filename} Log Element screenshot saved as: ${filename} Capture Screenshot With Timestamp [Documentation] Capture screenshot with current timestamp in filename ${timestamp}= Get Current Date result_format=%Y%m%d_%H%M%S ${filename}= Set Variable screenshot_${timestamp}.png Capture Page Screenshot ${filename} RETURN ${filename} Set Screenshot Directory [Arguments] ${directory_path} [Documentation] Set custom directory for screenshots Set Screenshot Directory ${directory_path} Log Screenshot directory set to: ${directory_path} # Text Retrieval Operations Get Element Text Value [Arguments] ${locator} [Documentation] Get text content from an element Wait Until Element Is Visible ${locator} 10s ${text}= Get Text ${locator} RETURN ${text} Get Input Field Value [Arguments] ${locator} [Documentation] Get value from input field Wait Until Element Is Visible ${locator} 10s ${value}= Get Value ${locator} RETURN ${value} Switch To Window By Title [Documentation] Switch to browser window by title [Arguments] ${expected_title} @{windows}= Get Window Handles FOR ${window} IN @{windows} Switch Window ${window} ${title}= Get Title IF '${title}' == '${expected_title}' RETURN END END Fail Window with title '${expected_title}' not found Switch To Window By URL [Documentation] Switch to browser window by URL pattern [Arguments] ${url_pattern} @{windows}= Get Window Handles FOR ${window} IN @{windows} Switch Window ${window} ${current_url}= Get Location IF '${url_pattern}' in '${current_url}' RETURN END END Fail Window with URL pattern '${url_pattern}' not found Close Other Windows [Documentation] Close all windows except the current one ${main_window}= Get Window Handles ${main_window}= Get From List ${main_window} 0 @{all_windows}= Get Window Handles FOR ${window} IN @{all_windows} IF '${window}' != '${main_window}' Switch Window ${window} Close Window END END Switch Window ${main_window} Get Page Performance Metrics [Documentation] Get basic page performance metrics using JavaScript ${load_time}= Execute Javascript return window.performance.timing.loadEventEnd - window.performance.timing.navigationStart ${dom_ready}= Execute Javascript return window.performance.timing.domContentLoadedEventEnd - window.performance.timing.navigationStart ${metrics}= Create Dictionary load_time=${load_time} dom_ready=${dom_ready} [Return] ${metrics} Scroll To Element Smoothly [Documentation] Scroll to element with smooth animation [Arguments] ${locator} Execute Javascript ... var element = document.evaluate("${locator}", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; ... if (element) { element.scrollIntoView({behavior: 'smooth', block: 'center'}); } Check Element Visibility Percentage [Documentation] Check what percentage of element is visible in viewport [Arguments] ${locator} ${visibility}= Execute Javascript ... var element = document.evaluate("${locator}", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; ... if (!element) return 0; ... var rect = element.getBoundingClientRect(); ... var viewport = {width: window.innerWidth, height: window.innerHeight}; ... var visible = Math.max(0, Math.min(rect.right, viewport.width) - Math.max(rect.left, 0)) * ... Math.max(0, Math.min(rect.bottom, viewport.height) - Math.max(rect.top, 0)); ... var total = rect.width * rect.height; ... return total > 0 ? (visible / total * 100).toFixed(2) : 0; [Return] ${visibility} Take Element Screenshot [Documentation] Take screenshot of specific element [Arguments] ${locator} ${filename}=element_screenshot.png Capture Element Screenshot ${locator} ${filename} [Return] ${filename} Get All List Labels [Arguments] ${locator} [Documentation] Get all available option labels from dropdown Wait Until Element Is Visible ${locator} 10s ${labels}= Get List Items ${locator} RETURN ${labels} Get Page Title [Documentation] Get current page title ${title}= Get Title RETURN ${title} Get Current URL [Documentation] Get current page URL ${url}= Get Location RETURN ${url} Get Page Source [Documentation] Get complete page source HTML ${source}= Get Source RETURN ${source} # Window Management Operations Get Current Window Position [Documentation] Get current window position coordinates ${position}= Get Window Position Log Current window position: ${position} RETURN ${position} Set Window Position [Arguments] ${x} ${y} [Documentation] Set window position to specific coordinates Set Window Position ${x} ${y} Log Window position set to: ${x}, ${y} Get Current Window Size [Documentation] Get current window size dimensions ${size}= Get Window Size Log Current window size: ${size} RETURN ${size} Set Window Size [Arguments] ${width} ${height} [Documentation] Set window size to specific dimensions Set Window Size ${width} ${height} Log Window size set to: ${width}x${height} Center Window On Screen [Documentation] Center the browser window on screen ${screen_width}= Execute JavaScript return screen.width; ${screen_height}= Execute JavaScript return screen.height; ${window_width}= Set Variable 1200 ${window_height}= Set Variable 800 ${x}= Evaluate (${screen_width} - ${window_width}) // 2 ${y}= Evaluate (${screen_height} - ${window_height}) // 2 Set Window Size ${window_width} ${window_height} Set Window Position ${x} ${y} Minimize Browser Window [Documentation] Minimize the browser window Execute JavaScript window.blur(); Restore Window Size [Arguments] ${width}=1024 ${height}=768 [Documentation] Restore window to default size Set Window Size ${width} ${height} Maximize Browser Window # Performance and Logging Operations Get Browser Console Logs [Documentation] Retrieve browser console logs ${logs}= Get Browser Logs Log Many @{logs} RETURN ${logs} Log Performance Metrics [Documentation] Log browser performance metrics ${navigation_timing}= Execute JavaScript return JSON.stringify(performance.getEntriesByType('navigation')[0]); ${paint_timing}= Execute JavaScript return JSON.stringify(performance.getEntriesByType('paint')); Log Navigation Timing: ${navigation_timing} Log Paint Timing: ${paint_timing} Measure Page Load Time [Documentation] Measure and return page load time in milliseconds ${load_time}= Execute JavaScript return performance.getEntriesByType('navigation')[0].loadEventEnd - performance.getEntriesByType('navigation')[0].navigationStart; Log Page load time: ${load_time} ms RETURN ${load_time} Get Network Performance [Documentation] Get network performance information ${network_info}= Execute JavaScript ... return JSON.stringify({ ... connection: navigator.connection || navigator.mozConnection || navigator.webkitConnection, ... onLine: navigator.onLine, ... cookieEnabled: navigator.cookieEnabled ... }); Log Network Info: ${network_info} RETURN ${network_info} Set Browser Implicit Wait [Arguments] ${timeout}=10s [Documentation] Set implicit wait timeout for element finding Set Browser Implicit Wait ${timeout} Log Browser implicit wait set to: ${timeout} Log Browser Information [Documentation] Log comprehensive browser information ${user_agent}= Execute JavaScript return navigator.userAgent; ${viewport}= Execute JavaScript return window.innerWidth + 'x' + window.innerHeight; ${screen_resolution}= Execute JavaScript return screen.width + 'x' + screen.height; ${color_depth}= Execute JavaScript return screen.colorDepth; Log User Agent: ${user_agent} Log Viewport Size: ${viewport} Log Screen Resolution: ${screen_resolution} Log Color Depth: ${color_depth} Monitor Page Resources [Documentation] Monitor and log page resource loading ${resources}= Execute JavaScript ... var resources = performance.getEntriesByType('resource'); ... var resourceInfo = resources.map(function(resource) { ... return { ... name: resource.name, ... type: resource.initiatorType, ... size: resource.transferSize, ... duration: resource.duration ... }; ... }); ... return JSON.stringify(resourceInfo); Log Page Resources: ${resources} RETURN ${resources} Clear Browser Performance Data [Documentation] Clear browser performance timing data Execute JavaScript performance.clearResourceTimings(); Execute JavaScript performance.clearMarks(); Execute JavaScript performance.clearMeasures(); Log Browser performance data cleared # Enhanced Screenshot Operations with Elements Compare Screenshots [Arguments] ${baseline_screenshot} ${current_screenshot} ${threshold}=0.95 [Documentation] Compare two screenshots (requires additional image comparison library) Log Comparing ${baseline_screenshot} with ${current_screenshot} # Note: This would require additional image comparison library like Pillow # For now, just logging the comparison request Take Screenshot On Failure [Documentation] Take screenshot when test fails (for teardown use) ${test_name}= Get Variable Value ${TEST_NAME} unknown_test ${timestamp}= Get Current Date result_format=%Y%m%d_%H%M%S ${filename}= Set Variable failure_${test_name}_${timestamp}.png Capture Page Screenshot ${filename} Log Failure screenshot saved: ${filename} Take Element Screenshot With Highlight [Arguments] ${locator} ${filename}=highlighted_element.png [Documentation] Take screenshot of element with visual highlight Wait Until Element Is Visible ${locator} 10s # Add visual highlight Execute JavaScript arguments[0].style.border = '3px solid red'; ARGUMENTS ${locator} Sleep 0.5s Capture Element Screenshot ${locator} ${filename} # Remove highlight Execute JavaScript arguments[0].style.border = ''; ARGUMENTS ${locator} Log Highlighted element screenshot saved: ${filename} """ return template @mcp.tool() def create_performance_monitoring_test() -> str: """Generate Robot Framework performance monitoring test code. Returns complete .robot file content as text - does not execute.""" return """*** Settings *** Library SeleniumLibrary Library Collections Library DateTime Library OperatingSystem *** Variables *** ${PERFORMANCE_THRESHOLD_LOAD} 3000 # 3 seconds ${PERFORMANCE_THRESHOLD_INTERACTIVE} 2000 # 2 seconds ${PERFORMANCE_THRESHOLD_PAINT} 1000 # 1 second *** Test Cases *** Website Performance Test [Documentation] Comprehensive website performance testing [Tags] performance load-time metrics # Start performance monitoring ${test_start}= Get Current Date result_format=epoch Open Browser ${TEST_URL} chrome options=add_argument("--enable-precise-memory-info") # Wait for page to fully load Wait Until Page Does Not Contain Loading timeout=30s Sleep 2s # Allow all resources to load # Collect performance metrics ${metrics}= Collect Performance Metrics # Validate performance thresholds Validate Performance Thresholds ${metrics} # Test page interactions performance Test Page Interaction Performance # Generate performance report Generate Performance Report ${metrics} Close Browser *** Keywords *** Collect Performance Metrics [Documentation] Collect comprehensive performance metrics # Navigation timing metrics ${navigation_timing}= Execute Javascript ... var timing = window.performance.timing; ... return { ... 'dns_lookup': timing.domainLookupEnd - timing.domainLookupStart, ... 'tcp_connect': timing.connectEnd - timing.connectStart, ... 'request_response': timing.responseEnd - timing.requestStart, ... 'dom_processing': timing.domComplete - timing.domLoading, ... 'load_complete': timing.loadEventEnd - timing.navigationStart, ... 'dom_ready': timing.domContentLoadedEventEnd - timing.navigationStart, ... 'first_paint': timing.responseStart - timing.navigationStart ... }; # Paint timing metrics (if supported) ${paint_timing}= Execute Javascript ... if ('getEntriesByType' in window.performance) { ... var paints = window.performance.getEntriesByType('paint'); ... var result = {}; ... paints.forEach(function(paint) { ... result[paint.name.replace('-', '_')] = paint.startTime; ... }); ... return result; ... } ... return {}; # Memory usage (if supported) ${memory_info}= Execute Javascript ... if ('memory' in window.performance) { ... return { ... 'used_js_heap': window.performance.memory.usedJSHeapSize, ... 'total_js_heap': window.performance.memory.totalJSHeapSize, ... 'heap_limit': window.performance.memory.jsHeapSizeLimit ... }; ... } ... return {}; # Resource timing ${resource_count}= Execute Javascript ... if ('getEntriesByType' in window.performance) { ... var resources = window.performance.getEntriesByType('resource'); ... var types = {}; ... resources.forEach(function(resource) { ... var type = resource.initiatorType || 'other'; ... types[type] = (types[type] || 0) + 1; ... }); ... types['total'] = resources.length; ... return types; ... } ... return {}; # Combine all metrics ${all_metrics}= Create Dictionary Set To Dictionary ${all_metrics} navigation=${navigation_timing} Set To Dictionary ${all_metrics} paint=${paint_timing} Set To Dictionary ${all_metrics} memory=${memory_info} Set To Dictionary ${all_metrics} resources=${resource_count} [Return] ${all_metrics} Validate Performance Thresholds [Documentation] Validate performance against thresholds [Arguments] ${metrics} ${navigation}= Get From Dictionary ${metrics} navigation # Check load time ${load_time}= Get From Dictionary ${navigation} load_complete Should Be True ${load_time} < ${PERFORMANCE_THRESHOLD_LOAD} ... Page load time (${load_time}ms) exceeds threshold (${PERFORMANCE_THRESHOLD_LOAD}ms) # Check DOM ready time ${dom_ready}= Get From Dictionary ${navigation} dom_ready Should Be True ${dom_ready} < ${PERFORMANCE_THRESHOLD_INTERACTIVE} ... DOM ready time (${dom_ready}ms) exceeds threshold (${PERFORMANCE_THRESHOLD_INTERACTIVE}ms) # Check first paint time (if available) ${paint}= Get From Dictionary ${metrics} paint ${has_first_paint}= Run Keyword And Return Status Dictionary Should Contain Key ${paint} first_paint IF ${has_first_paint} ${first_paint}= Get From Dictionary ${paint} first_paint Should Be True ${first_paint} < ${PERFORMANCE_THRESHOLD_PAINT} ... First paint time (${first_paint}ms) exceeds threshold (${PERFORMANCE_THRESHOLD_PAINT}ms) END Test Page Interaction Performance [Documentation] Test performance of page interactions # Test scroll performance ${scroll_start}= Get Time epoch Execute Javascript window.scrollTo(0, document.body.scrollHeight); Sleep 0.5s Execute Javascript window.scrollTo(0, 0); ${scroll_end}= Get Time epoch ${scroll_time}= Evaluate (${scroll_end} - ${scroll_start}) * 1000 Log Scroll performance: ${scroll_time}ms Should Be True ${scroll_time} < 500 Scroll should be smooth (< 500ms) # Test click response time (if interactive elements exist) ${buttons}= Get WebElements css=button, input[type="submit"], .btn ${button_count}= Get Length ${buttons} IF ${button_count} > 0 ${button}= Get From List ${buttons} 0 ${click_start}= Get Time epoch Click Element ${button} Sleep 0.1s # Small delay to register interaction ${click_end}= Get Time epoch ${click_time}= Evaluate (${click_end} - ${click_start}) * 1000 Log Click response time: ${click_time}ms Should Be True ${click_time} < 200 Click response should be immediate (< 200ms) END Generate Performance Report [Documentation] Generate detailed performance report [Arguments] ${metrics} ${timestamp}= Get Current Date result_format=%Y-%m-%d_%H-%M-%S ${report_file}= Set Variable performance_report_${timestamp}.txt ${navigation}= Get From Dictionary ${metrics} navigation ${resources}= Get From Dictionary ${metrics} resources ${report_content}= Catenate SEPARATOR=\n ... PERFORMANCE TEST REPORT ... ========================= ... Test URL: ${TEST_URL} ... Test Time: ${timestamp} ... ... NAVIGATION TIMING: ... - DNS Lookup: ${navigation}[dns_lookup]ms ... - TCP Connect: ${navigation}[tcp_connect]ms ... - Request/Response: ${navigation}[request_response]ms ... - DOM Processing: ${navigation}[dom_processing]ms ... - Page Load Complete: ${navigation}[load_complete]ms ... - DOM Ready: ${navigation}[dom_ready]ms ... ... RESOURCE LOADING: ... - Total Resources: ${resources}[total] if 'total' in ${resources} else 'N/A' ... ... PERFORMANCE THRESHOLDS: ... - Load Time Threshold: ${PERFORMANCE_THRESHOLD_LOAD}ms ... - Interactive Threshold: ${PERFORMANCE_THRESHOLD_INTERACTIVE}ms ... - Paint Threshold: ${PERFORMANCE_THRESHOLD_PAINT}ms Create File ${report_file} ${report_content} Log Performance report saved to: ${report_file} Performance Comparison Test [Documentation] Compare performance across multiple test runs [Arguments] ${test_iterations}=3 @{all_results}= Create List FOR ${iteration} IN RANGE 1 ${test_iterations + 1} Log Running performance test iteration ${iteration} Open Browser ${TEST_URL} chrome Sleep 2s ${metrics}= Collect Performance Metrics Append To List ${all_results} ${metrics} Close Browser Sleep 5s # Wait between tests END # Calculate average performance ${avg_metrics}= Calculate Average Performance ${all_results} Log Average performance across ${test_iterations} runs: ${avg_metrics} Calculate Average Performance [Documentation] Calculate average performance from multiple test runs [Arguments] ${results_list} ${total_load}= Set Variable 0 ${total_dom}= Set Variable 0 ${count}= Get Length ${results_list} FOR ${result} IN @{results_list} ${navigation}= Get From Dictionary ${result} navigation ${load_time}= Get From Dictionary ${navigation} load_complete ${dom_time}= Get From Dictionary ${navigation} dom_ready ${total_load}= Evaluate ${total_load} + ${load_time} ${total_dom}= Evaluate ${total_dom} + ${dom_time} END ${avg_load}= Evaluate ${total_load} / ${count} ${avg_dom}= Evaluate ${total_dom} / ${count} ${averages}= Create Dictionary avg_load_time=${avg_load} avg_dom_ready=${avg_dom} [Return] ${averages} """ @mcp.tool() def create_data_driven_test(test_data_file: str = "test_data.csv") -> str: """Generate Robot Framework data-driven test template code. Returns .robot file content as text - does not execute.""" template = f"""*** Settings *** Library SeleniumLibrary Library DataDriver {test_data_file} encoding=utf-8 Test Template Login Test Template *** Variables *** ${{BROWSER}} Chrome ${{BASE_URL}} https://www.appurl.com *** Test Cases *** Login Test With ${{username}} And ${{password}} [Tags] data-driven login # Test case will be generated for each row in CSV *** Keywords *** Login Test Template [Arguments] ${{username}} ${{password}} ${{expected_result}} [Documentation] Template for data-driven login tests Open Browser ${{BASE_URL}} ${{BROWSER}} Maximize Browser Window Input Text id=user-name ${{username}} Input Text id=password ${{password}} Click Button id=login-button Run Keyword If '${{expected_result}}' == 'success' ... Wait Until Page Contains Element xpath=//span[@class='title'] 10s ... ELSE ... Wait Until Page Contains Element xpath=//h3[@data-test='error'] 10s [Teardown] Close Browser *** Comments *** # Create {test_data_file} with columns: username,password,expected_result # Example CSV content: # username,password,expected_result # standard_user,secret_sauce,success # locked_out_user,secret_sauce,error # invalid_user,wrong_password,error """ return template @mcp.tool() def create_api_integration_test(base_url: str, endpoint: str, method: str = "GET") -> str: """Generate Robot Framework API integration test code. Returns .robot file content as text - does not execute.""" try: validated_url = InputValidator.validate_url(base_url) template = Template("""*** Settings *** Library SeleniumLibrary Library RequestsLibrary Library Collections *** Variables *** $${BASE_URL} $base_url $${API_ENDPOINT} $endpoint $${BROWSER} Chrome *** Test Cases *** API UI Integration Test [Documentation] Test API and UI integration [Tags] integration api ui # API Setup and Validation Create Session api_session $${BASE_URL} $${api_response}= GET On Session api_session $${API_ENDPOINT} Status Should Be 200 $${api_response} $${response_data}= Set Variable $${api_response.json()} # UI Validation based on API data Open Browser $${BASE_URL} $${BROWSER} Maximize Browser Window # Validate UI reflects API data Wait Until Page Contains $${response_data['title']} 10s Page Should Contain $${response_data['description']} [Teardown] Run Keywords ... Close Browser AND ... Delete All Sessions *** Keywords *** Validate API Response Structure [Arguments] $${response} [Documentation] Validate API response has required fields Dictionary Should Contain Key $${response} id Dictionary Should Contain Key $${response} title Dictionary Should Contain Key $${response} description """) return template.substitute( base_url=validated_url, endpoint=endpoint, method=method.upper() ) except ValidationError as e: return f"# VALIDATION ERROR: {str(e)}\n# Please correct the input and try again." except Exception as e: return f"# UNEXPECTED ERROR: {str(e)}\n# Please contact support." @mcp.tool() def validate_robot_framework_syntax(robot_code: str) -> str: """Validate Robot Framework syntax and provide suggestions. Returns validation report as text - does not execute code.""" try: lines = robot_code.split('\n') errors = [] warnings = [] # Check for basic syntax issues for i, line in enumerate(lines, 1): line_stripped = line.strip() if not line_stripped or line_stripped.startswith('#'): continue # Check for proper section headers if line_stripped.startswith('***') and not line_stripped.endswith('***'): errors.append(f"Line {i}: Section header must end with '***'") # Check for proper variable syntax - should be ${variable} not ${{variable}} if '${{' in line and '}}' in line: warnings.append(f"Line {i}: Use ${{variable}} syntax instead of ${{{{variable}}}}") # Check for unclosed variable syntax if '${' in line and '}' not in line: errors.append(f"Line {i}: Unclosed variable syntax") # Check for proper spacing in variables section if line_stripped.startswith('${') and ' ' not in line: warnings.append(f"Line {i}: Variables should use 4 spaces between name and value") result = "# ROBOT FRAMEWORK SYNTAX VALIDATION\n\n" if errors: result += "## ERRORS (Must Fix):\n" result += '\n'.join(f"- {error}" for error in errors) + "\n\n" if warnings: result += "## WARNINGS (Recommended Fixes):\n" result += '\n'.join(f"- {warning}" for warning in warnings) + "\n\n" if not errors and not warnings: result += "✅ VALIDATION PASSED: No syntax errors found\n" elif not errors: result += "⚠️ VALIDATION PASSED WITH WARNINGS: No critical errors, but consider fixing warnings\n" else: result += "❌ VALIDATION FAILED: Critical errors found that must be fixed\n" return result except Exception as e: return f"# VALIDATION ERROR: {str(e)}" def main(): """Entry point for the Robot Framework MCP server""" import sys # Only print startup messages if not in stdio mode if len(sys.argv) > 1 and '--stdio' in sys.argv: # Running in stdio mode (MCP client mode) pass else: # Running in standalone mode print("Starting Robot Framework MCP server...") print("Available templates: appLocator, generic, bootstrap") print("Features: Input validation, configurable selectors, syntax validation") print("Advanced tools: API integration, data-driven testing, responsive testing") try: mcp.run() except Exception as e: if len(sys.argv) <= 1 or '--stdio' not in sys.argv: print(f"Error starting Robot framework MCP server: {e}") import traceback traceback.print_exc() raise if __name__ == "__main__": main()

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/sourcefuse/robotframework-mcp'

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