login
Authenticate with Open eClass via UoA's SSO by configuring username and password in your .env file. Enables AI agents to interact with the eClass platform securely.
Instructions
Log in to eClass using username/password from your .env file through UoA's SSO. Configure ECLASS_USERNAME and ECLASS_PASSWORD in your .env file.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| random_string | Yes | Dummy parameter for no-parameter tools |
Implementation Reference
- src/eclass_mcp_server/server.py:106-156 (registration)The @server.list_tools() decorator registers the 'login' tool with its schema and description.@server.list_tools() async def handle_list_tools() -> list[types.Tool]: """ List available eClass tools. """ return [ types.Tool( name="login", description="Log in to eClass using username/password from your .env file through UoA's SSO. Configure ECLASS_USERNAME and ECLASS_PASSWORD in your .env file.", inputSchema={ "type": "object", "properties": { "random_string": {"type": "string", "description": "Dummy parameter for no-parameter tools"}, }, "required": ["random_string"], }, ), types.Tool( name="get_courses", description="Get list of enrolled courses from eClass", inputSchema={ "type": "object", "properties": { "random_string": {"type": "string", "description": "Dummy parameter for no-parameter tools"}, }, "required": ["random_string"], }, ), types.Tool( name="logout", description="Log out from eClass", inputSchema={ "type": "object", "properties": { "random_string": {"type": "string", "description": "Dummy parameter for no-parameter tools"}, }, "required": ["random_string"], }, ), types.Tool( name="authstatus", description="Check authentication status with eClass", inputSchema={ "type": "object", "properties": { "random_string": {"type": "string", "description": "Dummy parameter for no-parameter tools"}, }, "required": ["random_string"], }, ), ]
- Input schema definition for the 'login' tool, requiring a dummy 'random_string' parameter.types.Tool( name="login", description="Log in to eClass using username/password from your .env file through UoA's SSO. Configure ECLASS_USERNAME and ECLASS_PASSWORD in your .env file.", inputSchema={ "type": "object", "properties": { "random_string": {"type": "string", "description": "Dummy parameter for no-parameter tools"}, }, "required": ["random_string"], }, ),
- src/eclass_mcp_server/server.py:176-210 (handler)The main handler function for the 'login' tool, which retrieves credentials from environment variables, calls the authentication logic, and formats the response.async def handle_login(arguments: Dict[str, Any]) -> List[types.TextContent]: """Handle login to eClass.""" # Check if already logged in if session_state.logged_in and session_state.is_session_valid(): return [ types.TextContent( type="text", text=f"Already logged in as {session_state.username}", ) ] # Reset session if needed if session_state.logged_in and not session_state.is_session_valid(): session_state.reset() # Get credentials from environment variables username = os.getenv('ECLASS_USERNAME') password = os.getenv('ECLASS_PASSWORD') if not username or not password: return [ types.TextContent( type="text", text="Error: Username and password must be provided in the .env file. Please set ECLASS_USERNAME and ECLASS_PASSWORD in your .env file.", ) ] logger.info(f"Attempting to log in as {username}") # Attempt login using the authentication module success, message = authentication.attempt_login(session_state, username, password) # Format and return the response return [authentication.format_login_response(success, message, username if success else None)]
- Core implementation of the login logic, handling the multi-step SSO authentication flow with eClass.def attempt_login(session_state, username: str, password: str) -> Tuple[bool, Optional[str]]: """ Attempt to log in to eClass using the SSO authentication flow. Args: session_state: The current session state username: eClass username password: eClass password Returns: Tuple of (success, error_message) """ try: # Step 1: Visit the eClass login form page response = session_state.session.get(session_state.login_form_url) response.raise_for_status() # Step 2: Find the SSO login button and follow it sso_link = html_parsing.extract_sso_link(response.text, session_state.base_url) if not sso_link: return False, "Could not find SSO login link on the login page" # Follow the SSO link response = session_state.session.get(sso_link) response.raise_for_status() # Step 3: Extract execution parameter and submit login form to CAS if 'sso.uoa.gr' not in response.url: return False, f"Unexpected redirect to {response.url}" execution, action, error_text = html_parsing.extract_cas_form_data(response.text, response.url) # Handle extraction errors if error_text and ('authenticate' in error_text.lower() or 'credentials' in error_text.lower()): return False, f"Authentication error: {error_text}" if not execution: return False, "Could not find execution parameter on SSO page" if not action: return False, "Could not find login form on SSO page" # Prepare login data login_data = { 'username': username, 'password': password, 'execution': execution, '_eventId': 'submit', 'geolocation': '' } # Submit login form response = session_state.session.post(action, data=login_data) response.raise_for_status() # Check for authentication errors if 'Πόροι Πληροφορικής ΕΚΠΑ' not in response.text and 'The credentials you provided cannot be determined to be authentic' not in response.text: logger.info("Successfully authenticated with SSO") else: # Re-parse to get error message if present execution, action, error_text = html_parsing.extract_cas_form_data(response.text, response.url) if error_text: return False, f"Authentication error: {error_text}" else: return False, "Authentication failed: Invalid credentials" # Step 4: Check if we've been redirected to eClass and verify login success if 'eclass.uoa.gr' in response.url: # Try to access portfolio page to verify login response = session_state.session.get(session_state.portfolio_url) response.raise_for_status() # Check if we can access the portfolio page if html_parsing.verify_login_success(response.text): session_state.logged_in = True session_state.username = username logger.info("Login successful, redirected to eClass portfolio") return True, None else: return False, "Could not access portfolio page after login" else: return False, f"Unexpected redirect after login: {response.url}" except requests.RequestException as e: logger.error(f"Request error during login: {str(e)}") return False, f"Network error during login process: {str(e)}" except Exception as e: logger.error(f"Login error: {str(e)}") return False, f"Error during login process: {str(e)}"
- Helper function to format the login response as MCP TextContent.def format_login_response(success: bool, message: Optional[str], username: Optional[str] = None) -> types.TextContent: """ Format login response for MCP. Args: success: Whether login was successful message: Error message if login failed username: Username if login succeeded Returns: Formatted MCP TextContent response """ if success: return types.TextContent( type="text", text=f"Login successful! You are now logged in as {username}.", ) else: return types.TextContent( type="text", text=f"Error: {message}", )