#!/usr/bin/env python3
"""
LMS MCP Server for University Student Portal Automation
This MCP server provides tools for:
- Logging into university LMS systems
- Checking attendance records
- Retrieving marks and grades
- Handling CAPTCHA challenges
- Managing session persistence
"""
import asyncio
import logging
from typing import Any, Sequence
from mcp import ClientSession, StdioServerParameters
from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import mcp.types as types
from lms_automation import LMSAutomation
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("lms-mcp")
# Create server instance
server = Server("lms-mcp")
# Global LMS automation instance
lms_automation = LMSAutomation()
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
"""List available LMS automation tools."""
return [
types.Tool(
name="login_to_lms",
description="Login to the university LMS system with credentials",
inputSchema={
"type": "object",
"properties": {
"username": {
"type": "string",
"description": "University username/student ID",
},
"password": {
"type": "string",
"description": "University password",
},
"lms_url": {
"type": "string",
"description": "URL of the LMS login page",
},
"save_session": {
"type": "boolean",
"description": "Whether to save session for future use",
"default": True,
},
},
"required": ["username", "password", "lms_url"],
},
),
types.Tool(
name="check_attendance",
description="Check attendance records for a specific subject or all subjects",
inputSchema={
"type": "object",
"properties": {
"subject_code": {
"type": "string",
"description": "Subject code (optional, if not provided will get all subjects)",
},
"semester": {
"type": "string",
"description": "Semester (optional, defaults to current semester)",
},
},
"required": [],
},
),
types.Tool(
name="get_marks",
description="Retrieve marks and grades for subjects",
inputSchema={
"type": "object",
"properties": {
"subject_code": {
"type": "string",
"description": "Subject code (optional, if not provided will get all subjects)",
},
"exam_type": {
"type": "string",
"description": "Type of exam (e.g., 'midterm', 'final', 'assignment')",
"enum": ["midterm", "final", "assignment", "quiz", "all"],
},
"semester": {
"type": "string",
"description": "Semester (optional, defaults to current semester)",
},
},
"required": [],
},
),
types.Tool(
name="get_timetable",
description="Get class timetable/schedule",
inputSchema={
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "Specific date in YYYY-MM-DD format (optional, defaults to today)",
},
"week": {
"type": "boolean",
"description": "Get full week schedule",
"default": False,
},
},
"required": [],
},
),
types.Tool(
name="solve_captcha",
description="Solve CAPTCHA challenges using OCR",
inputSchema={
"type": "object",
"properties": {
"captcha_image_path": {
"type": "string",
"description": "Path to the CAPTCHA image file",
},
"preprocess": {
"type": "boolean",
"description": "Whether to preprocess the image for better OCR",
"default": True,
},
},
"required": ["captcha_image_path"],
},
),
types.Tool(
name="logout_lms",
description="Logout from the LMS system and clear session",
inputSchema={"type": "object", "properties": {}, "required": []},
),
types.Tool(
name="view_application_status",
description="View current status of all submitted applications",
inputSchema={
"type": "object",
"properties": {
"application_type": {
"type": "string",
"description": "Filter by application type (optional)",
},
"semester": {
"type": "string",
"description": "Filter by semester (optional)",
},
},
"required": [],
},
),
types.Tool(
name="create_application",
description="Create and submit a new application to the university",
inputSchema={
"type": "object",
"properties": {
"application_type": {
"type": "string",
"description": "Type of application to create (required)",
},
"subject": {
"type": "string",
"description": "Subject/title of the application",
},
"message": {
"type": "string",
"description": "Detailed message/content of the application",
},
"additional_data": {
"type": "object",
"description": "Any additional form data needed for the application",
},
},
"required": ["application_type"],
},
),
]
@server.call_tool()
async def handle_call_tool(
name: str, arguments: dict[str, Any] | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""Handle tool calls for LMS automation."""
if arguments is None:
arguments = {}
try:
if name == "login_to_lms":
result = await lms_automation.login(
username=arguments["username"],
password=arguments["password"],
lms_url=arguments["lms_url"],
save_session=arguments.get("save_session", True),
)
return [types.TextContent(type="text", text=str(result))]
elif name == "check_attendance":
result = await lms_automation.get_attendance(
subject_code=arguments.get("subject_code"),
semester=arguments.get("semester"),
)
return [types.TextContent(type="text", text=str(result))]
elif name == "get_marks":
result = await lms_automation.get_marks(
subject_code=arguments.get("subject_code"),
exam_type=arguments.get("exam_type", "all"),
semester=arguments.get("semester"),
)
return [types.TextContent(type="text", text=str(result))]
elif name == "get_timetable":
result = await lms_automation.get_timetable(
date=arguments.get("date"), week=arguments.get("week", False)
)
return [types.TextContent(type="text", text=str(result))]
elif name == "solve_captcha":
result = await lms_automation.solve_captcha(
captcha_image_path=arguments["captcha_image_path"],
preprocess=arguments.get("preprocess", True),
)
return [types.TextContent(type="text", text=str(result))]
elif name == "logout_lms":
result = await lms_automation.logout()
return [types.TextContent(type="text", text=str(result))]
elif name == "view_application_status":
result = await lms_automation.view_application_status(
application_type=arguments.get("application_type"),
semester=arguments.get("semester"),
)
return [types.TextContent(type="text", text=str(result))]
elif name == "create_application":
result = await lms_automation.create_application(
application_type=arguments["application_type"],
subject=arguments.get("subject"),
message=arguments.get("message"),
additional_data=arguments.get("additional_data"),
)
return [types.TextContent(type="text", text=str(result))]
else:
raise ValueError(f"Unknown tool: {name}")
except Exception as e:
logger.error(f"Error in tool {name}: {str(e)}")
return [types.TextContent(type="text", text=f"Error: {str(e)}")]
async def main():
"""Main function to run the MCP server."""
# Run the server using stdin/stdout streams
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="lms-mcp",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
if __name__ == "__main__":
asyncio.run(main())