Skip to main content
Glama
README.md
# METU SAIS API & Multi-Tenant MCP Server

A modern **Model Context Protocol (MCP)** server and **FastAPI** service for Middle East Technical University (METU / ODTÜ) Student Affairs Information System (SAIS / `student.metu.edu.tr`).

Connects your AI assistants (such as **OpenCode**, **Claude Desktop**, and **Cursor**) directly to METU SAIS with real authentication, structured data parsing, and **stateless multi-tenant remote streaming**.

---

## Features

- **Multi-Tenant Architecture**: Supports both local single-user runs and remote multi-tenant serving over Server-Sent Events (SSE) / HTTP. Remote users pass their own credentials via headers or query parameters without storing any passwords on the server.
- **SSO Authentication**: Handles the real METU SSO login flow (`/sso/backend/request/user/signin`) and session token validation.
- **Proxy Scraper & HTML Parser**: Automatically establishes authenticated proxy sessions (`/proxy/Student_Information/`) and parses student records into structured JSON.
- **MCP Server (MCP SDK)**: Exposes typed tools over standard I/O (`stdio`), Server-Sent Events (`sse`), or Streamable HTTP.
- **FastAPI REST Service**: Alternative HTTP REST backend for API integrations.
- **Privacy & Security**: Zero credential logging or exposure.

---

## Available MCP Tools

| Tool | Description |
|---|---|
| `get_student_info` | Fetches student ID, name, department, semester, GPA/CGPA, academic status, and advisor. |
| `get_schedule` | Fetches weekly class schedule (days, hours, course codes, course names, classrooms, instructors). |
| `get_transcript` | Fetches academic transcript across all semesters with courses, credits, grades, and semester GPA statistics. |
| `get_announcements` | Fetches active portal announcements. |
| `create_connection` | Creates an in-memory connection record. |
| `list_connections` | Lists all saved connection records. |

---

## Installation & Setup

### 1. Prerequisites
- Python >= 3.11
- [uv](https://github.com/astral-sh/uv) (recommended) or `pip`

### 2. Install Dependencies
```bash
# Using uv (recommended)
uv sync

# Or using pip
pip install -e .
```

### 3. Local Configuration (Optional for Single-User Runs)
For local single-user runs, copy `.env.example` to `.env`:

```bash
cp .env.example .env
```

Edit `.env`:
```ini
SAIS_USERNAME=your_metu_username
SAIS_PASSWORD=your_metu_password
LOCALE=tr
PORT=8300
```

> [!NOTE]
> `.env` is listed in `.gitignore` and will never be committed. Remote multi-tenant users do **not** need a `.env` file on the server because they supply their credentials per-session via request headers or URL parameters.

---

## Running Multi-Tenant Remote MCP (Online)

### Start the Server with SSE Transport

```bash
# Direct MCP SSE server:
uv run metu-sais-mcp --transport sse --host 0.0.0.0 --port 8300

# Or via FastAPI (serves both REST API and MCP SSE at /sse and /messages/):
uv run uvicorn api.main:app --host 0.0.0.0 --port 8300
```

### Connecting Remote Clients (Claude Desktop / Cursor / OpenCode)

#### Option 1: Custom Headers (Recommended)
```json
{
  "mcpServers": {
    "metu-sais": {
      "url": "https://mcp.yourdomain.com/sse",
      "headers": {
        "X-SAIS-Username": "e123456",
        "X-SAIS-Password": "student_secret_password"
      }
    }
  }
}
```

#### Option 2: Basic Authentication Header
```json
{
  "mcpServers": {
    "metu-sais": {
      "url": "https://mcp.yourdomain.com/sse",
      "headers": {
        "Authorization": "Basic <base64(username:password)>"
      }
    }
  }
}
```

#### Option 3: URL Query Parameters
```json
{
  "mcpServers": {
    "metu-sais": {
      "url": "https://mcp.yourdomain.com/sse?u=e123456&p=student_secret_password"
    }
  }
}
```

---

## Local Usage with OpenCode & Claude Desktop

### Project-Level Usage with OpenCode
When starting OpenCode inside this repository:
```bash
opencode
```
OpenCode will automatically connect to the `metu-sais` MCP server defined in [`opencode.json`](./opencode.json).

### Local Stdio Standalone Run
```bash
uv run metu-sais-mcp
```

---

## REST API

To start the FastAPI server:

```bash
uv run uvicorn api.main:app --port 8300 --reload
```

Interactive API documentation will be available at:
- **Swagger UI**: [http://localhost:8300/docs](http://localhost:8300/docs)
- **Health check**: [http://localhost:8300/healthz](http://localhost:8300/healthz)

### REST Endpoints
- `GET /api/sais/info` — Student information
- `GET /api/sais/schedule` — Class schedule
- `GET /api/sais/transcript` — Transcript history
- `GET /api/sais/announcements` — Portal announcements
- `GET|POST|PUT|DELETE /api/connections` — Connection management CRUD

*(In multi-tenant mode, pass `X-SAIS-Username` and `X-SAIS-Password` headers or `?u=...&p=...` query parameters to any REST endpoint).*

---

## Testing

Run the automated test suite with pytest:

```bash
uv run pytest -v
```

The test suite includes:
- Unit tests for HTML parsers (`parse_student_info`, `parse_schedule`, `parse_transcript`, `parse_announcements`).
- MCP tool registration and tool calling tests.
- Multi-tenant credential extraction and session isolation tests.
- FastAPI REST endpoint tests.
- Live integration tests against METU SAIS when credentials are configured.

---

## Project Structure

```text
├── sais/
│   ├── auth.py           # Multi-tenant credential resolver and session store
│   ├── connector.py      # Core SAIS authentication, proxy scraping, and HTML parsers
│   └── connections.py    # Shared in-memory connection store
├── sais_mcp/
│   └── server.py         # MCP multi-tenant server (stdio, sse, streamable-http)
├── api/
│   └── main.py           # FastAPI REST API + mounted MCP SSE app
├── tests/
│   ├── test_connector.py # Scraper and HTML parser tests
│   ├── test_mcp_server.py# MCP server and multi-tenant SSE isolation tests
│   └── test_api.py       # FastAPI tests
├── .env.example          # Environment variable template
├── opencode.json         # OpenCode MCP configuration
├── pyproject.toml        # Package dependencies and CLI entrypoints
└── README.md             # Project documentation
```

---

## License

MIT License.

TDQS

A3.5/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a clearly distinct resource or action: student info, schedule, transcript, announcements, and connection records. There is no meaningful overlap that would cause an agent to select the wrong tool.

Naming Consistency5/5

Tool names follow a consistent snake_case verb_noun pattern: get_student_info, get_schedule, get_transcript, get_announcements, create_connection, list_connections. The only minor variation is singular vs plural nouns, but the pattern is predictable.

Tool Count5/5

Six tools is a well-scoped set for a student portal MCP server. It covers the core read operations and adds connection management without bloating the surface.

Completeness3/5

The student-facing read operations are solid, but connection management is incomplete: create_connection and list_connections have no corresponding update or delete operation, creating a lifecycle dead end. Minor gaps like course-level detail or exam results are less critical but still absent.

Maintenance

ActivityMaintained
ResponsivenessNo issues