Skip to main content
Glama
README.md
# DTU Calendar MCP

A local MCP server for DTU Learn calendar feeds. It lets an AI client read classes, assignments, deadlines, exams, and recent calendar changes without browser automation.

The server only reads DTU. It writes fetched events to a local SQLite cache so queries stay fast and still work when Learn is temporarily unavailable.

## What it can answer

- What is on my DTU calendar this week?
- Which assignments or peer-feedback deadlines are coming up?
- What does Learn show for course `12345`?
- Did an event move, change, or disappear?
- Is the feed configured, current, or stale?

The MCP server provides six tools:

- `list_upcoming`
- `list_deadlines`
- `get_calendar_item`
- `get_course_overview`
- `get_calendar_changes`
- `get_feed_status`

Calendar data does not prove course registration, exam registration, completion, grades, or passed ECTS.

## Requirements

- Python 3.11 or newer
- A private DTU Learn iCalendar subscription URL
- An MCP client that can run a local stdio server

## Install

Clone the repository, then open a terminal in it.

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e .
dtu-calendar-mcp --version
```

On macOS or Linux, activate the environment with `source .venv/bin/activate`.

## Add your DTU Learn feed

The subscription URL is a bearer secret. Anyone with the URL may be able to read your calendar. Do not commit it, paste it into an issue, or put it in MCP JSON.

Get the calendar URL:

1. Open DTU Learn.
2. Go to **Calendar**.
3. Choose **Subscribe**.
4. Copy the full subscription URL.

Get the tasks URL:

1. Open **Calendar** in DTU Learn.
2. Go to **Tasks**.
3. Choose **Subscribe**.
4. Copy the full tasks subscription URL.

Create a `.env` file in the repository root. Git ignores this file.

```dotenv
DTU_LEARN_ICS_URL=<paste the full calendar subscription URL>
DTU_LEARN_TASKS_ICS_URL=<paste the full tasks subscription URL>
```

Paste the whole URL, not only the value after `token=`. The tasks feed is optional.

Check the configuration:

```powershell
dtu-calendar-mcp doctor
dtu-calendar-mcp sync
```

Real environment variables take precedence over `.env` values. This is useful if your MCP client has its own secret store.

If a feed URL has been exposed, revoke or regenerate it in DTU Learn before using this project.

See [configuration.md](docs/configuration.md) for persistent environment settings, timezone options, and cache location.

## Connect an MCP client

Use this server definition in a client that inherits the environment containing your feed URL:

```json
{
  "mcpServers": {
    "dtu-calendar": {
      "command": "dtu-calendar-mcp",
      "args": ["serve"]
    }
  }
}
```

The repository also contains a Codex plugin under [`plugins/dtu-calendar-mcp`](plugins/dtu-calendar-mcp). The plugin contains no feed URL or personal data.

## How refreshes work

The list, item, and course tools refresh stale feeds before reading. A refresh contacts the configured DTU URL and updates the local cache. Change and status queries only read the cache.

Every result includes `data_as_of`, `stale`, `warnings`, and `configured_sources`. Check them before treating an empty result as proof that nothing is scheduled. If a feed suddenly becomes empty or loses more than half its events, the server keeps the previous snapshot and marks the refresh as suspect.

The default cache is `%LOCALAPPDATA%\dtu-calendar-mcp\calendar.sqlite3` on Windows and `~/.cache/dtu-calendar-mcp/calendar.sqlite3` elsewhere.

## Limits

The parser supports the recurrence patterns normally used by calendar feeds, including daily, weekly, monthly, exclusions, added dates, and individual occurrence changes. It bounds feed size, event text, recurrence expansion, and MCP output so one bad item cannot fill the disk or hang the server.

Read [limits.md](docs/limits.md) for exact behavior and [privacy.md](docs/privacy.md) before connecting an institutional feed to a model provider.

## Development

```powershell
python -m pip install -e ".[dev]"
python -m pytest -q
python -m ruff check .
powershell -ExecutionPolicy Bypass -File .\scripts\check-release.ps1
```

The test suite uses synthetic calendar data. Do not add real student feeds or subscription tokens to fixtures.

## License

MIT. See [LICENSE](LICENSE).

TDQS

A3.8/5.0

Scored across 6 tools

Disambiguation4/5

Most tools have clearly distinct purposes: listing upcoming items, listing inferred deadlines, fetching single items, summarizing by course, showing cache changes, and reporting feed status. The only potential confusion is between list_upcoming and list_deadlines, since deadlines are a subset of upcoming items, but the descriptions clarify the distinction.

Naming Consistency4/5

The naming pattern is mostly consistent verb_noun: list_deadlines, get_calendar_item, get_course_overview, get_calendar_changes, get_feed_status. list_upcoming deviates slightly because 'upcoming' is an adjective rather than a noun object, but the pattern is otherwise uniform and predictable.

Tool Count5/5

Six tools is well-scoped for a calendar-oriented MCP server. Each tool addresses a distinct need: browsing upcoming items, filtering deadlines, retrieving details, summarizing by course, detecting changes, and checking feed health. No tool feels redundant or unnecessary.

Completeness5/5

The tool surface covers the apparent read-only calendar domain well: listing upcoming content, surfacing deadlines, retrieving individual items, summarizing by course, tracking changes over time, and reporting cache/source status. There are no obvious missing operations that would create dead ends for an agent.

Maintenance

ActivityMaintained
ResponsivenessNo issues