Skip to main content
Glama
index.ts9.43 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import puppeteer from "puppeteer"; // Create server instance const server = new McpServer({ name: "Sakarya University - MCP Server", version: "1.0.0", capabilities: { resources: {}, tools: {}, }, } ); // Register SABIS login and grade scraping tool server.tool( "get-grades", "Get grades of a student by logging into SABIS system", { username: { type: "string", description: "University username/student ID", required: true, }, password: { type: "string", description: "University password", required: true, }, }, async (args) => { const username = process.env.USERNAME; const password = process.env.PASSWORD; if (!username || !password) { throw new Error("Username and password are required. Please give them to your mcp host as environment variables."); } try { // Launch browser const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); // Set user agent to avoid detection await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'); // Navigate to login page const loginUrl = 'https://login.sabis.sakarya.edu.tr/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DSabis.Obs.WebUI%26redirect_uri%3Dhttps%253A%252F%252Fobs.sabis.sakarya.edu.tr%252Fsignin-oidc%26response_type%3Dcode%2520id_token%26scope%3Dopenid%2520profile%2520roles%26response_mode%3Dform_post%26nonce%3D638865586101464397.OGQ0YjdhMGMtMGZmMi00ODk3LWFmODgtOTM2OTYzZTAzODlmODNjNmU2NjMtMzE5NC00NDRkLTk5ZDgtN2ZjNDFiMDdkODEy%26state%3DCfDJ8Gcl3K2dWixFslXAXXJUu9siJv-d9xny73TJ_oNmfNCHjyLoCmjoAzfiDhoK7KLxlpp3IzAiNXG6D-Cx3HthDH1Y-tQ-_twGSAleUsm5kMYqLvnUyrl0vAFb08AdnsnshEKpu1pvWzNNhhRs1lMeBu1VcH4L32HkV3GrUeqyLsBkwGr4XAS2E9pOJo7PmN4u1862OOFA7do_fekek7QvIG4ucd8IrjJVStpELrnmqHAytn9rsgFfHtVl_f3BdJ9n09qNdJ8vzFGuB52J9McKyLRipk4o3egdmmuXSPpsIIYNprGQMAvvjYawKT14HGHGmw%26x-client-SKU%3DID_NET6_0%26x-client-ver%3D6.36.0.0'; console.log('Navigating to login page...'); await page.goto(loginUrl, { waitUntil: 'networkidle2' }); // Wait for login form to load await page.waitForSelector('#Username', { timeout: 10000 }); // Fill username field await page.type('#Username', username); console.log('Username filled'); // Fill password field await page.type('#Password', password); console.log('Password filled'); // Submit the form by clicking the login button await page.click('button[name="button"][value="login"]'); console.log('Login form submitted'); // Wait for navigation after login await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 }); // Check if login was successful by looking for specific elements or URLs const currentUrl = page.url(); console.log('Current URL after login:', currentUrl); if (currentUrl.includes('obs.sabis.sakarya.edu.tr')) { console.log('Login successful, now accessing grades...'); // Try to navigate to grades page try { // Navigate directly to the grades page console.log('Navigating to grades page...'); await page.goto('https://obs.sabis.sakarya.edu.tr/Ders'); await page.waitForSelector('.card', { timeout: 10000 }); // Extract detailed grades data const gradesData = await page.evaluate(() => { const courseCards = document.querySelectorAll('.card-custom'); const courses: string[] = []; courseCards.forEach(card => { // Extract course code from symbol const courseCodeEl = card.querySelector('.symbol-label'); const courseCode = courseCodeEl?.textContent?.trim() || 'Unknown Code'; // Extract course name const courseNameEl = card.querySelector('.text-dark.font-weight-bolder'); const courseName = courseNameEl?.textContent?.trim() || 'Unknown Course'; // Extract course group info const courseGroupEl = card.querySelector('.text-muted.font-weight-bold'); const courseGroup = courseGroupEl?.textContent?.trim() || ''; let courseInfo = `\n=== ${courseCode} - ${courseName} ===`; if (courseGroup) { courseInfo += `\nGrup: ${courseGroup}`; } // Extract grades table const table = card.querySelector('table'); if (table) { const rows = table.querySelectorAll('tbody tr'); let hasGrades = false; rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length >= 3) { const percentage = cells[0]?.textContent?.trim() || ''; const assessmentType = cells[1]?.textContent?.trim() || ''; const grade = cells[2]?.textContent?.trim() || ''; if (percentage || assessmentType || grade) { hasGrades = true; if (assessmentType.includes('Başarı Notu')) { courseInfo += `\n📋 Final Grade: ${grade}`; } else { courseInfo += `\n• ${assessmentType}${percentage ? ` (${percentage}%)` : ''}: ${grade || 'Not Available'}`; } } } }); if (!hasGrades) { courseInfo += '\n• No grades available yet'; } } else { courseInfo += '\n• No grade information found'; } courses.push(courseInfo); }); return courses.length > 0 ? courses : ['No course data found']; }); // Also get semester info const semesterInfo = await page.evaluate(() => { const yearSelect = document.querySelector('#yil') as HTMLSelectElement; const semesterSelect = document.querySelector('#donem') as HTMLSelectElement; const year = yearSelect?.value || 'Unknown'; const semester = semesterSelect?.selectedOptions[0]?.textContent || 'Unknown'; return `Academic Year: ${year} - Semester: ${semester}`; }); await browser.close(); return { content: [ { type: "text", text: `Login successful! 🎓\n\n${semesterInfo}\n${gradesData.join('\n')}`, }, ], }; } catch (gradesError) { console.error('Error accessing grades page:', gradesError); await browser.close(); return { content: [ { type: "text", text: `Login successful, but couldn't access grades page. Error: ${gradesError instanceof Error ? gradesError.message : String(gradesError)}`, }, ], }; } } else { // Login failed const errorMessage = await page.evaluate(() => { const errorElements = document.querySelectorAll('.alert-danger, .error, .validation-summary-errors'); return Array.from(errorElements).map(el => el.textContent?.trim()).join('; '); }); await browser.close(); return { content: [ { type: "text", text: `Login failed. Error: ${errorMessage || 'Invalid credentials or unexpected redirect'}`, }, ], }; } } catch (error) { console.error('Scraping error:', error); return { content: [ { type: "text", text: `Error during scraping: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }, ); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Weather MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });

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/hanifisenturk/sabis-mcp-server'

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