Skip to main content
Glama

UofT MCP

A small Python MCP server for the public UofT Timetable Builder API, with read-only Degree Explorer and ACORN access and reusable UofT login. It exposes seven tools by default over local stdio using the official MCP Python SDK. Discover operations on demand, batch independent reads, and select or page short-lived results without repeatedly fetching them. The existing 25-tool interface remains available with --tool-profile legacy. Both profiles support the same timetable, Degree Explorer, ACORN, and authentication capabilities.

See the efficiency and architecture guide for migration, worked examples, result limits, extension instructions, and reproducible benchmarks.

Work in progress: Degree Explorer and three basic ACORN reads are available in this checkout. Degree Explorer writes and ACORN enrolment changes are not implemented.

Public timetable tools require no login, API key, database, web server, or environment variables. This is an unofficial wrapper; it does not enroll students or write to ACORN. Saving a timetable creates an anonymous public share link on the Timetable Builder, not a personal account record.

Read the release notes for the current version's scope and known limitations.

Connect an MCP Client

Install uv, then add this configuration to any client that supports mcpServers:

{
  "mcpServers": {
    "uoft-timetable": {
      "command": "uvx",
      "args": ["uoft-mcp@latest"],
      "env": {
        "UV_HTTP_TIMEOUT": "300"
      }
    }
  }
}

Restart the client after saving its configuration. On Windows, if the client cannot find uvx, restart it after installing uv or replace "uvx" with the absolute path reported by where.exe uvx.

uvx downloads the published package into an isolated environment and starts the uoft-mcp command. No repository clone, virtual environment setup, API key, server URL, or listening port is needed. The first start can take longer while uv downloads Python and the dependencies; later starts use its cache.

To pin a release instead of following the newest release, use "args": ["uoft-mcp==0.6.0"].

Related MCP server: LettuceMeet MCP

Local Development

From a clone of this repository:

uv python install 3.13
uv sync --locked --managed-python

uv sync creates .venv, installs the package and development tools, and uses the committed uv.lock. .python-version selects Python 3.13; the package supports Python 3.13 and newer.

Run the local checkout with:

uv run --locked python -m uoft_mcp

The installed uoft-mcp command is another entry point. The process waits for an MCP client on stdin; a blank terminal is expected. Use Ctrl+C to stop a manual run. Stdout carries protocol messages only, and logging goes to stderr.

Connect Degree Explorer and ACORN

Degree Explorer tools reuse your saved UofT session to read academic records and existing plans. To try them from this checkout, install Chromium once:

uv run --locked python -m uoft_mcp auth setup

Connect your MCP client to this local checkout (the published package does not gain branch changes until a release):

{
  "mcpServers": {
    "uoft": {
      "command": "uv",
      "args": ["--directory", "C:/path/to/UofT-MCP", "run", "--locked", "python", "-m", "uoft_mcp"]
    }
  }
}

Ask your assistant to "Connect my UofT account to Degree Explorer and ACORN." Complete the official UofT login and Duo prompts in the dedicated Chromium window. After each service first verifies your login, the browser stays on that service for five more seconds, rechecks access, and captures the latest session before moving to the next service or closing. Login state is encrypted locally and reused after browser closure and MCP restarts, while UofT still accepts it. Passwords and Duo codes belong only on the official pages, never in chat or config.

Use "Check my UofT connection" to verify access, or "Forget my saved UofT session" to delete local access. See authentication setup and behavior for terminal commands, memory-only sessions, expiry, and troubleshooting.

Default compact tools

Tool

Purpose

uoft_discover

Search read operations; request their argument schemas only when needed.

uoft_read

Batch up to eight validated reads, with optional selection and bounded previews.

uoft_result

Inspect, filter, project, or page an in-memory snapshot without refetching.

save_timetable

Explicitly create an anonymous public timetable share.

uoft_login

Start or reuse official browser login.

uoft_auth_status

Check connection state and login progress.

uoft_forget_session

Delete saved access and invalidate private snapshots.

For example, call uoft_discover with {"query":"get_course_details","detail":"schemas"}, then uoft_read with:

{"requests":[
  {"operation":"get_course_details","arguments":{"course_code":"CSC108H1"}},
  {"operation":"get_course_details","arguments":{"course_code":"CSC148H1"}}
]}

Results contain per-operation data or errors, retrieval timestamps, and transient handles. Arrays default to 20 rows, with a maximum of 100; selected JSON data is bounded to 16 KiB per item. Oversized objects return an explicit overview. Use uoft_result with the handle and a narrower JSON Pointer, projection, or page. Snapshots expire after five minutes and are never persisted to disk. Private snapshots are invalidated on authentication changes. See selection and result contracts before interpreting partial results.

Legacy tools and operation names

Run uv run --locked python -m uoft_mcp --tool-profile legacy for the original interface. The following read names are also valid operation values inside compact uoft_read. save_timetable and the three auth controls remain direct tools and cannot be included in a read batch.

Tool

Arguments and purpose

get_current_sessions

No arguments. Get current session IDs; skip entries with header: true.

get_reference_data

No arguments. Get campus, division, delivery-mode, and sorting values.

get_divisions

No arguments. List recognized faculty/division codes.

search_departments

Required term keyword and divisions code.

search_course_titles

Required term, divisions, and sessions strings. Optional lower_threshold=50, upper_threshold=200.

get_course_details

Required course_code; optional section_code of F, S, or Y. The returned course id is required by generate_timetable.

search_courses

Optional code/title, section, description, division, session, campus, delivery, and pagination filters.

generate_timetable

Required plans array. Each plan has courses (course_id plus activity_types), optional preference of early, balanced, or late, and optional blocked_times.

save_timetable

Required timetable object with sessions, timetables, and plans. Returns the share id plus a share_url.

retrieve_timetable

Required share_id from save_timetable.

uoft_login

Optional service of degree_explorer, acorn, or both (default), plus remember=true. Starts or reuses official browser login and returns while you complete Duo.

uoft_auth_status

Optional refresh=false. Reports connection and login progress; refresh=true checks both services without opening a browser.

uoft_forget_session

No arguments. Cancels login and removes locally saved UofT session state and its encryption key.

acorn_get_eligible_registrations

Optional fields list. Read eligible registration periods; selection applies to each registration.

acorn_get_dashboard_courses

Optional fields list. Read dashboard enrolled courses for the current session.

acorn_get_student_registration_info

Optional fields list. Read registration/financial-hold status, person ID, and upcoming-exams flag.

degree_explorer_get_academic_history

No arguments. Read course history, sessions, marks, and requirements after Degree Explorer login.

degree_explorer_get_student_data

No arguments. Read the payload used by Degree Explorer's Current Status page.

degree_explorer_get_student_record

No arguments. Read the record payload used by Current Status; it is not a certified transcript.

degree_explorer_get_student_user_data

No arguments. Read menu/session user metadata.

degree_explorer_get_student_menu

No arguments. Read available Degree Explorer navigation entries.

degree_explorer_get_messages

No arguments. Read the UI string catalog, not a student inbox.

degree_explorer_get_session_timeouts

No arguments. Read client timeout settings; they do not extend a session.

degree_explorer_get_planner

No arguments. Read existing planner timelines and primary-plan flags.

degree_explorer_get_cell_details

No arguments. Read the unparameterized planner popup endpoint; it cannot target a cell.

See Degree Explorer tools and workflow for all nine authenticated reads, their fixed API routes, and limitations. Each underlying operation accepts {} and reads only the connected student's account.

See ACORN tools and workflow for the three basic reads and field-selection examples. They reuse the connected account without opening a login browser.

In legacy mode, each successful lookup, generation, and retrieve call returns one text block containing the complete upstream JSON. The wrapper preserves fields and arrays, including upstream payload and status envelopes. It does not summarize or truncate course data. save_timetable keeps the upstream share object and adds share_url.

Compact mode performs discovery, bounded batching, and local result selection before returning data. It does not run arbitrary code or supply a code-execution sandbox. Measured fixture comparisons distinguish model-facing output savings from upstream request counts.

Legacy example workflow

  1. Call get_current_sessions with {} and select a non-header entry's value.

  2. Call get_divisions or get_reference_data for valid filter codes.

  3. Call search_course_titles with these arguments, substituting the session value:

{"term": "CSC108", "divisions": "ARTSC", "sessions": "SESSION_ID_FROM_STEP_1"}
  1. Use the returned exact course code in get_course_details:

{"course_code": "CSC108H1", "section_code": "F"}
  1. For filtered, paginated results, call search_courses:

{
  "course_code": "CSC108H1",
  "divisions": ["ARTSC"],
  "sessions": ["SESSION_ID_FROM_STEP_1"],
  "page": 1,
  "page_size": 2
}

search_courses also accepts course_title, course_section_code, search_course_description, campuses, delivery_modes, and direction (asc or desc). Pages start at 1, page size defaults to 20, and sorting defaults to asc. Omitted collection filters become empty arrays. Course codes should be exact; use autocomplete for prefixes or course_title for keyword searches.

  1. Copy each selected offering's id from get_course_details into generate_timetable. Activity types are the section types to fill, such as Lecture, Tutorial, or Practical. Preference defaults to balanced. Optional blocked intervals use weekday names and 24-hour HH:MM times:

{
  "plans": [
    {
      "courses": [
        {
          "course_id": "COURSE_ID_FROM_STEP_4",
          "activity_types": ["Lecture", "Tutorial"]
        }
      ],
      "preference": "early",
      "blocked_times": [{"day": "Monday", "start": "8:00", "end": "10:00"}]
    }
  ]
}

Use one plan per term. A Fall and Winter year is two plans. The solver returns chosen sections; it does not enroll students.

  1. Store a Timetable Builder state with save_timetable. The timetable object must include sessions, timetables, and plans in the frontend's serialized shape. The tool returns id and share_url (https://ttb.utoronto.ca/#!/?t=...).

  2. Reload that share later with retrieve_timetable:

{"share_id": "SHARE_ID_FROM_STEP_7"}

Checks

uv run --locked pytest -q
uv run --locked ruff check .
uv run --locked ruff format --check .

The PR workflow runs these checks on pull requests targeting main or master, using Python 3.13 on Ubuntu. New commits cancel an older run for the same PR. Tests need no Chromium installation or UofT credentials.

The tests run offline. They cover the twenty-five legacy tools and seven compact tools, request mapping, raw JSON preservation, validation, HTTP errors, timeouts, connection errors, invalid JSON, shared-client cleanup, MCP discovery, and actual stdio subprocesses. Authentication tests cover encrypted persistence, restoration, browser lifecycle, expiry, locking, CLI controls, and sanitized status results using synthetic state and fake backends. Degree Explorer tests also cover all nine GET mappings, MCP schemas and annotations, JSON preservation, sanitized errors, response disposal, saved-session reuse, and serialization with login and forget. These reads have not been verified against a live authenticated account in this change. ACORN tests additionally cover compact JSON, field selection, and expected root types. Both profiles have offline regression coverage; compact tests add batching, selection, snapshot lifecycle, and efficiency budgets. A live ACORN read on 2026-09-19 returned a login redirect; authenticated live verification remains pending.

Previous timetable verification: 50 tests passed. Live checks of lookup, generateYear, tiny/shorten, and tiny/retrieve succeeded, including generating CSC258H1 and CSC311H1, saving an anonymous share, and retrieving that share. Live requests are deliberately not part of the test suite, so tests remain reproducible.

Current authentication verification is recorded in AUTHENTICATION.md.

API Notes

Project layout

The MCP entry points remain uoft_mcp.server and python -m uoft_mcp. Service-specific code is grouped below the package so integrations can grow independently:

uoft_mcp/
├── timetable_builder/  # public Timetable Builder client and API reference
├── degree_explorer/    # allowlisted, read-only Degree Explorer client
├── acorn/              # allowlisted ACORN reads and field selection
├── utilities/          # shared authentication, browser, and secure session storage
├── server.py           # MCP tool registration and application wiring
└── cli.py              # stdio server and terminal authentication commands
  • The supplied Timetable Builder reference remains the original reference. Live checks found two missing details: pagination starts at 1, and paginated search requires an empty departmentProps array when not filtering by department. The wrapper supplies it.

  • generateYear accepts an array of plans. Each plan needs course id values from get_course_details, sections with name: "*" and a type, fitnessFunctionOption (MORNING_WEIGHTED, BALANCED, or AFTERNOON_WEIGHTED), and blockedOff intervals using weekday numbers 1-5 (Monday-Friday) and milliseconds since midnight.

  • tiny/shorten stores the official https://ttb.utoronto.ca/#!/? URL for a serialized timetable and returns { "id": "..." }. tiny/retrieve?id= loads it. These are anonymous share records, not ACORN enrolment.

  • Get division codes from the API. For example, the live API uses ERIN and SCAR for Mississauga and Scarborough, rather than the reference's UTM and UTSC examples.

  • Even one course can have a large response because all its sections are included. Choose narrow filters and small page sizes. Compact selection reduces returned data; neither profile fetches extra upstream pages automatically.

  • HTTP failures become MCP tool errors containing the endpoint and status code. UofT may return HTTP 404 for no matching courses. Timeouts, connection failures, and malformed JSON get their own readable errors. No automatic retries occur.

  • This API is not covered by an official support guarantee. Changes upstream may require updating the mappings. Successful HTTP responses are preserved as supplied, including any application-level status messages inside their JSON.

Available Tools

7 tools
save_timetableA

Store a serialized Timetable Builder state and return a public share URL.

timetable must be the TTB state object with sessions, timetables, and plans. The wrapper posts the official ttb.utoronto.ca URL for that state and returns the upstream share id plus a https://ttb.utoronto.ca/#!/?t= link. This creates an anonymous share record only; it does not enroll students.

ParametersJSON Schema
NameRequiredDescriptionDefault
timetableYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds real context beyond the annotations: it discloses that the wrapper posts to the upstream ttb.utoronto.ca service, returns an upstream share id and a share link, and that only an anonymous share record is created. It does not mention that repeated saves create duplicate records, which the non-idempotent annotation hints at but the prose never confirms.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and return value, then the parameter requirement, then scope caveats. It is slightly wordy about the upstream URL mechanics, but every sentence contributes to correct invocation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description correctly documents the return shape (upstream share id plus an https://ttb.utoronto.ca/#!/?t= link). Annotations already cover the safety profile (write, non-idempotent, non-destructive), so remaining gaps are minor, e.g. behavior on repeated saves.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the single parameter is an unconstrained nested object (additionalProperties: true), so the description carries the burden. It does so well by specifying that "timetable" must be the TTB state object containing sessions, timetables, and plans, giving the agent the shape the empty schema omits.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it stores a serialized Timetable Builder state and returns a public share URL. That verb (store/save) plus the share-URL return clearly separates it from siblings like generate_timetable and retrieve_timetable, which produce or fetch rather than persist.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clarifies one boundary explicitly ("creates an anonymous share record only; it does not enroll students"), which tells the agent this is not an enrollment action. However, it never says when to prefer this over siblings such as retrieve_timetable or what precondition the state must come from, so usage is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

uoft_auth_statusA
Read-onlyIdempotent

Get connection and login progress without exposing credentials or student records.

refresh=True checks both services using saved cookies; it never opens a login window. A saved cookie is not proof of a valid session. last_verified is historical, not expiry.

ParametersJSON Schema
NameRequiredDescriptionDefault
refreshNo

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds genuinely new behavioral context: saved cookies are checked without opening a window, a saved cookie is not proof of a valid session, and last_verified is historical rather than an expiry — all of which shape how the agent interprets results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the core purpose and followed by the parameter behavior and output caveats. Every sentence adds distinct value; none restates the name or schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, non-destructive status read with no output schema, the description covers enough: scope of the read, refresh behavior, and the interpretation caveat for last_verified. It could say a bit more about what fields the status response contains for an agent to act on, but the essentials are present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter has 0% schema description coverage, so the description must carry the meaning — and it does, explaining that refresh=True re-checks both services against saved cookies with no login window. It does not describe the default (refresh=false) behavior explicitly, leaving one small gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: 'Get connection and login progress.' It also scopes what is NOT returned ('without exposing credentials or student records'), which helps separate it from data-returning siblings like get_course_details. It never names an alternative tool explicitly, so sibling differentiation is implied rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states a concrete use condition: 'refresh=True checks both services using saved cookies; it never opens a login window.' This tells the agent when to set the flag and clarifies that this tool is not the login path (uoft_login is). No explicit when-not guidance or named alternative is given, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

uoft_discoverB
Read-onlyIdempotent

Find read operations locally; request schemas before calling unfamiliar operations.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryNo
detailNodescriptions
serviceNo

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint, covering safety and idempotency. The description adds that it finds read operations locally and requests schemas, providing context that it is a discovery tool with no side effects. However, it does not disclose any additional behavioral traits beyond what annotations already convey, so a 3 is appropriate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two concise sentences that clearly state the purpose and the recommended usage pattern. It is front-loaded with the primary function and is free of redundant information. It earns a 4 for being efficient and well-structured, though it could potentially be expanded with minimal additional detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has 4 parameters, no output schema, and relies on annotations for safety. The description does not explain how to use the parameters, what the return format looks like, or provide examples. For a discovery tool, an agent would need to know what information it returns (e.g., names, descriptions, schemas) and how to specify queries. The description lacks this essential context, making it incomplete for effective use. A score of 2 is appropriate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage, so the description must compensate for the missing parameter documentation. However, the description does not mention any of the parameters (query, limit, detail, service). The parameter names and enum values (e.g., detail: names/descriptions/schemas; service: timetable/degree_explorer/acorn) provide some self-explanatory meaning, but the description adds no value to understanding the parameters. Given the low coverage, the description fails to compensate, so a score of 2 is justified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool finds read operations locally and requests schemas before calling unfamiliar operations. This identifies it as a discovery tool for available read operations and schema retrieval, distinguishing it from sibling tools like uoft_read (which likely performs the actual read). However, the term 'locally' is slightly ambiguous, so a 4 is appropriate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool should be used to discover read operations and to request schemas before invoking unfamiliar operations, providing a clear context for use. However, it does not explicitly mention alternatives or when not to use it, such as specifying that uoft_read is for actual reads or that this tool should precede any unfamiliar operation. The guidance is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

uoft_forget_sessionA
DestructiveIdempotent

Cancel login and delete this MCP's local sessions and encryption key for both apps.

This closes managed authentication resources. It is not university-wide logout.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is covered. The description adds meaningful specifics beyond that: exactly what is destroyed (local sessions and the encryption key) and for which scope (this MCP, both apps, not university-wide).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the destructive action and then the scope caveat. No filler, and the most important constraint is not buried at the end.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-param, no-output-schema tool whose annotations carry the safety hints, the description is nearly complete, covering action and scope. The only unresolved ambiguity is what 'both apps' refers to and whether re-login is required afterward.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (cancel/delete) and resource (local sessions and encryption key), and explicitly scopes it away from a university-wide logout. An agent can distinguish this from uoft_login and uoft_auth_status without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear context for use ('cancel login', local-only scope) and an explicit boundary ('not university-wide logout'), which tells the agent when this is inappropriate. It stops short of naming the alternative action (e.g. re-authenticating via uoft_login) after forgetting.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

uoft_loginA

Start official UofT browser login, returning immediately while you complete Duo.

service is degree_explorer, acorn, or both. Saved sessions are reused when valid. Newly verified browser sessions wait five seconds, recheck access, and save the latest session before continuing or closing. Status stays in_progress during the wait. remember=False starts a fresh memory-only session without deleting saved sessions. Never supply passwords or MFA codes through tools. Check uoft_auth_status for progress.

ParametersJSON Schema
NameRequiredDescriptionDefault
serviceNoboth
rememberNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses important behavior beyond the annotations: immediate return, a five-second wait with rechecking, saving the latest session, status staying in_progress, non-deletion on remember=False, and a security rule against supplying passwords or MFA codes. This gives the agent a strong model of side effects and timing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but not bloated; the core behavior is front-loaded, followed by parameter-specific details and a safety note. Each sentence earns its place and no filler is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, but the description covers what happens immediately, how status behaves during the wait, what remember=False does, and where to check progress. This is sufficient for an agent to invoke the tool and follow up correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It lists service options (degree_explorer, acorn, or both) and explains the meaningful semantics of remember=False as a fresh memory-only session without deleting saved sessions. Service values mostly echo the schema enum, but the remember explanation adds real value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Start official UofT browser login') and a clear resource, and immediately signals that it is asynchronous by returning while Duo is completed. It is clearly distinguishable from sibling auth-state tools like uoft_auth_status or uoft_forget_session.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete invocation context: saved sessions are reused when valid, remember=False starts a fresh memory-only session, and the agent should check uoft_auth_status for progress. It does not explicitly contrast with sibling session tools, but the intended usage is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

uoft_readD
Read-onlyIdempotent

Batch discovered reads with optional selection. Snapshot handles last five minutes.

ParametersJSON Schema
NameRequiredDescriptionDefault
requestsYes

TDQS

D1.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The only behavioral addition beyond annotations is 'Snapshot handles last five minutes,' which is cryptic and could mean a time window, a snapshot mode, or something else. Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, so the description adds little meaningful transparency. It does not contradict annotations, but the added statement is too vague to be useful.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short, but brevity results in under-specification rather than conciseness. Neither sentence is actionable: 'Batch discovered reads' is vague, and 'Snapshot handles last five minutes' is ambiguous. Valuable space is wasted on unclear phrasing instead of essential usage details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With a complex nested input schema, no output schema, and zero parameter description coverage, the description is grossly inadequate. It does not explain how to structure requests, what operations are supported, what selection modes do, or what the tool returns. An agent would be unable to invoke the tool correctly based on the description alone.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for the undocumented parameters, but it does not. The phrase 'optional selection' vaguely references the selection object, yet it leaves the required 'requests' array and nested 'operation', 'arguments', and 'selection' fields completely unexplained. An agent cannot determine valid operation values, selection modes, or filtering semantics from the description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description says 'Batch discovered reads' but never names a specific resource or clear verb-object relationship. It is ambiguous what 'discovered' refers to and how this differs from uoft_discover or uoft_result. The name uoft_read hints at reading, but the description itself does not clarify the tool's core function.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is given about when to use this tool versus any sibling. The description does not mention uoft_discover, uoft_result, or any alternative, nor does it state prerequisites or exclusions. The word 'Batch' implies bulk operations, but there is no concrete context for choosing this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

uoft_resultB
Read-onlyIdempotent

Select or page a snapshot without network access. Refetch when the handle expires.

ParametersJSON Schema
NameRequiredDescriptionDefault
handleYes
selectionNo

TDQS

B3.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and non-destructive behavior. The description adds valuable behavioral context: 'without network access' and the note about handle expiration requiring a refetch. These details go beyond what annotations provide and help the agent understand operational constraints, though it doesn't describe the exact failure mode when the handle expires.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise—two short sentences with no filler. The primary behavior ('Select or page a snapshot without network access') is front-loaded, and the handle-expiry note is secondary. Every word earns its place; it is efficient and well-structured for quick scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has a complex optional 'selection' parameter and no output schema, the description is far too sparse. It fails to explain what 'selection' does, what the expected return format is, or how to construct a valid selection. The note about handle expiration is useful but insufficient for an agent to call this tool correctly without additional inference. The schema's nested description partially compensates, but overall the description leaves critical gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the tool description does not mention any parameters. Neither 'handle' nor 'selection' is explained in the description. The nested Selection schema has its own description, but the tool description itself provides no guidance on what these parameters mean or how they should be used, leaving a significant gap given the lack of schema-level descriptions for the top-level properties.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Select or page') on a resource ('a snapshot') and notes the key constraint of no network access. It is clear enough to distinguish from general read operations, though it does not explicitly differentiate from sibling tools like uoft_read. The term 'snapshot' is somewhat vague, but the core purpose is understandable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies that this tool is used when you already have a handle (since it mentions 'Refetch when the handle expires'), but it provides no explicit guidance on when to use this tool versus alternatives like uoft_read or uoft_discover. There are no stated exclusions or conditions for choosing this tool, leaving the agent to infer usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 21 tool updatesv0.6.0
    • Removeddegree_explorer_get_academic_history
    • Removeddegree_explorer_get_cell_details
    • Removeddegree_explorer_get_messages
    • Removeddegree_explorer_get_planner
    • Removeddegree_explorer_get_session_timeouts
    • Removeddegree_explorer_get_student_data
    • Removeddegree_explorer_get_student_menu
    • Removeddegree_explorer_get_student_record
    • Removeddegree_explorer_get_student_user_data
    • Removedgenerate_timetable
    • Removedget_course_details
    • Removedget_current_sessions
    • Removedget_divisions
    • Removedget_reference_data
    • Removedretrieve_timetable
    • Removedsearch_course_titles
    • Removedsearch_courses
    • Removedsearch_departments
    • Addeduoft_discover
    • Addeduoft_read
    • Addeduoft_result
  2. 9 tool updatesv0.4.0
    • Addeddegree_explorer_get_academic_history
    • Addeddegree_explorer_get_cell_details
    • Addeddegree_explorer_get_messages
    • Addeddegree_explorer_get_planner
    • Addeddegree_explorer_get_session_timeouts
    • Addeddegree_explorer_get_student_data
    • Addeddegree_explorer_get_student_menu
    • Addeddegree_explorer_get_student_record
    • Addeddegree_explorer_get_student_user_data
  3. 3 tool updatesv0.3.2
    • Addeduoft_auth_status
    • Addeduoft_forget_session
    • Addeduoft_login
  4. 10 tool updatesv0.2.0
    • First observedgenerate_timetable
    • First observedget_course_details
    • First observedget_current_sessions
    • First observedget_divisions
    • First observedget_reference_data
    • First observedretrieve_timetable
    • First observedsave_timetable
    • First observedsearch_course_titles
    • First observedsearch_courses
    • First observedsearch_departments

TDQS

B3.2/5.0

Scored across 7 tools

Disambiguation4/5

Tools are mostly distinct: authentication flow (login, auth_status, forget_session), discovery (discover), read operations (read, result), and saving (save_timetable). Minor overlap between 'read' and 'result' as both handle retrieval, but result is specifically for snapshot paging, so it's acceptable.

Naming Consistency3/5

Most tools follow a 'uoft_' prefix with verb or noun, but 'save_timetable' breaks the pattern. Verbs are inconsistent (login, discover, read, forget_session) vs nouns (auth_status, result). Still readable, but not a uniform convention.

Tool Count5/5

7 tools is well within the ideal 3-15 range. Each tool serves a clear purpose: authentication management, discovery, reading, result handling, and saving. No redundancy or bloat.

Completeness3/5

The set covers authentication, discovery, reading, and saving timetable states, but lacks explicit tools for creating or modifying timetables (only saves a provided state). This may require agents to construct states themselves, a notable gap for a 'Timetable Builder' server.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    Local-first Rutgers course planning assistant that answers 'what should I take and when?' using a CP-SAT solver, real transcript data, and live SOC data, connected to Claude via MCP.
    13
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Unofficial MCP tools for LettuceMeet to create polls, view responses, and find meeting overlaps via CLI or MCP server.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Remote MCP connector providing live, structured access to CMU Courses public data including courses, prerequisites, schedules, instructors, gen-eds, and final-exam times to help students plan schedules.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for querying the public UCLA Schedule of Classes. It provides tools to list terms, subject areas, search courses, and get detailed enrollment, waitlist, and section information.
    -