{
  "name": "NTUMods",
  "url": "https://ntumods.com",
  "api": "WebMCP: document.modelContext.registerTool (W3C Web Machine Learning Community Group draft)",
  "note": "These tools exist only inside a browser tab on this site that supports WebMCP. They act on the visitor's own saved plans. This file is for discovery.",
  "tools": [
    {
      "name": "get_site_info",
      "title": "About NTUMods",
      "description": "Describe NTUMods and its current data: the latest semester, what each page does, and the limits of the data (no live vacancies, no registration). Call this first if you are unsure what the site can do.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_current_page",
      "title": "Current page",
      "description": "Return the path, title and description of the page the user is looking at, plus the module code when on a module page.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "navigate_to",
      "title": "Open a page",
      "description": "Open a page of this site in the user's tab. Accepts a site path such as /timetable, /mods, /exams, /tools/cgpa-planner or /mods/SC1003. This reloads the page, which re-registers these tools.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "pattern": "^/[A-Za-z0-9/_\\-?=&.%]*$",
            "description": "Site-relative path starting with /."
          }
        },
        "required": [
          "path"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "list_semesters",
      "title": "List semesters",
      "description": "List every semester with data (from AY2014 Semester 1), newest first, and say which is the current one.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "search_modules",
      "title": "Search modules",
      "description": "Search NTU modules by code or title. Filters combine. Returns code, title, AU, school and categories. Use get_module for the full record and get_module_indexes for class times.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "Text to find in the code or title, e.g. 'SC10' or 'data structures'."
          },
          "school": {
            "type": "string",
            "description": "School code such as CSC, EEE, BUS, MATH or SSS."
          },
          "category": {
            "type": "string",
            "enum": [
              "CORE",
              "GLOAD",
              "MLOAD",
              "GERP",
              "CNY",
              "USP"
            ],
            "description": "CORE = programme core, GLOAD = BDE, MLOAD = minor, GERP = general education."
          },
          "level": {
            "type": "string",
            "enum": [
              "1",
              "2",
              "3",
              "4",
              "5",
              "6",
              "7"
            ],
            "description": "Level, taken from the first digit of the numeric part of the code."
          },
          "au": {
            "type": "number",
            "description": "Exact AU value, e.g. 3."
          },
          "currentSemesterOnly": {
            "type": "boolean",
            "description": "Only modules offered in the latest semester. Default true."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "description": "Default 25."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_module",
      "title": "Get module",
      "description": "Full record for one module: description, AU, grade type, prerequisites, mutually exclusive modules, programme restrictions, exam and semesters offered. Prerequisites are groups: every group must be satisfied and any module inside a group satisfies it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "untrustedContentHint": true
      }
    },
    {
      "name": "get_module_indexes",
      "title": "Get class indexes",
      "description": "Every class index of a module with its timeslots (type, group, day, time, venue, remarks). An index is a class group; pick one per module for a timetable.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "year": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "description": "Academic year, e.g. 2026. Defaults to the latest semester."
          },
          "semester": {
            "type": "string",
            "enum": [
              "1",
              "2",
              "S",
              "T"
            ],
            "description": "1, 2, S (special term 1) or T (special term 2)."
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "find_compatible_indexes",
      "title": "Find indexes that fit",
      "description": "For a module, list which indexes clash with the user's saved plan and which fit. Use before add_module_to_timetable to explain the choice.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_exam",
      "title": "Get exam",
      "description": "Exam date, start and end time and duration for one module. Venues are not published in this data.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "year": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "description": "Academic year, e.g. 2026. Defaults to the latest semester."
          },
          "semester": {
            "type": "string",
            "enum": [
              "1",
              "2",
              "S",
              "T"
            ],
            "description": "1, 2, S (special term 1) or T (special term 2)."
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "list_exams",
      "title": "List exams",
      "description": "List exams for a semester, filtered by a text query on code or name and/or a date (YYYY-MM-DD, or written out like '23 November 2026').",
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "date": {
            "type": "string"
          },
          "year": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "description": "Academic year, e.g. 2026. Defaults to the latest semester."
          },
          "semester": {
            "type": "string",
            "enum": [
              "1",
              "2",
              "S",
              "T"
            ],
            "description": "1, 2, S (special term 1) or T (special term 2)."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 200,
            "description": "Default 50."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_timetable",
      "title": "Get timetable",
      "description": "The user's saved plan: modules with the chosen index and slots, custom blocks, total AU, clashes (including exam-day clashes) and locked modules.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "list_plans",
      "title": "List plans",
      "description": "Summarise all five saved plans (module codes, AU, clash count) and say which one is active.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "add_module_to_timetable",
      "title": "Add module",
      "description": "Add a module to a plan. With no index, picks the first index that does not clash. Replaces the index if the module is already in the plan. Reversible with remove_module_from_timetable.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "index": {
            "type": "string",
            "description": "Index key, e.g. 10172. Optional."
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "import_modules",
      "title": "Add several modules",
      "description": "Add many modules at once by code, choosing a non-clashing index for each. Reports which were added and which were skipped and why.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "codes": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{2,12}$",
              "description": "NTU module code, e.g. SC1003. Case-insensitive."
            },
            "minItems": 1,
            "maxItems": 20
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "codes"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "remove_module_from_timetable",
      "title": "Remove module",
      "description": "Remove a module from a plan.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "set_module_index",
      "title": "Change index",
      "description": "Switch a module already in the plan to another index.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "index": {
            "type": "string"
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "code",
          "index"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "set_module_lock",
      "title": "Lock or unlock module",
      "description": "Lock a module's index so generate_timetables keeps it, or unlock it so the generator may change it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "locked": {
            "type": "boolean"
          }
        },
        "required": [
          "code",
          "locked"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "switch_plan",
      "title": "Switch plan",
      "description": "Change which of the five plans the user is viewing and editing.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "plan"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "clear_plan",
      "title": "Clear plan",
      "description": "Remove every module and custom block from a plan. Asks the user to confirm first.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "consequentialHint": true
      }
    },
    {
      "name": "add_custom_block",
      "title": "Add custom block",
      "description": "Add a personal commitment (gym, CCA, work, lunch) as a custom block on the active plan. It clash-checks against modules.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "sessions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "day": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 7,
                  "description": "1 = Monday ... 7 = Sunday."
                },
                "startTime": {
                  "type": "integer",
                  "minimum": 700,
                  "maximum": 2230,
                  "description": "24-hour time as a number, e.g. 830 or 1430."
                },
                "endTime": {
                  "type": "integer",
                  "minimum": 700,
                  "maximum": 2230,
                  "description": "24-hour time as a number, e.g. 830 or 1430."
                },
                "type": {
                  "type": "string",
                  "maxLength": 20,
                  "description": "Label such as Gym or CCA. Defaults to the block name."
                },
                "venue": {
                  "type": "string",
                  "maxLength": 60
                }
              },
              "required": [
                "day",
                "startTime",
                "endTime"
              ],
              "additionalProperties": false
            },
            "minItems": 1,
            "maxItems": 30
          }
        },
        "required": [
          "name",
          "sessions"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "remove_custom_block",
      "title": "Remove custom block",
      "description": "Remove a custom block from the active plan by id (see get_timetable).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "get_generation_config",
      "title": "Get generation preferences",
      "description": "The saved preferences generate_timetables uses by default.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "update_generation_config",
      "title": "Update generation preferences",
      "description": "Merge preferences into the saved generation config, or reset it. Times are HHMM numbers on a 30-minute grid.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "config": {
            "type": "object",
            "properties": {
              "dayStartTime": {
                "type": "integer",
                "minimum": 700,
                "maximum": 2230,
                "description": "24-hour time as a number, e.g. 830 or 1430."
              },
              "dayEndTime": {
                "type": "integer",
                "minimum": 700,
                "maximum": 2230,
                "description": "24-hour time as a number, e.g. 830 or 1430."
              },
              "maxConsecutiveSlots": {
                "type": "integer",
                "minimum": 0,
                "maximum": 12,
                "description": "Longest run of back-to-back 30-minute slots. 0 = no limit."
              },
              "minGapBetweenClasses": {
                "type": "integer",
                "minimum": 0,
                "maximum": 4,
                "description": "Minimum gap between classes, in 30-minute units."
              },
              "lunchBreaks": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "day": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 7,
                      "description": "1 = Monday ... 7 = Sunday."
                    },
                    "startTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    },
                    "endTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    }
                  },
                  "required": [
                    "day",
                    "startTime",
                    "endTime"
                  ],
                  "additionalProperties": false
                },
                "description": "Windows to keep clear, at most one per day."
              },
              "loadPreference": {
                "type": "string",
                "enum": [
                  "compact",
                  "spread"
                ]
              },
              "skippableTypes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "LEC",
                    "TUT",
                    "LAB",
                    "SEM",
                    "DES",
                    "PRJ"
                  ]
                },
                "description": "Class types the generator may leave out of a combination."
              },
              "blacklistedTimeslots": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "day": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 7,
                      "description": "1 = Monday ... 7 = Sunday."
                    },
                    "startTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    },
                    "endTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    }
                  },
                  "required": [
                    "day",
                    "startTime",
                    "endTime"
                  ],
                  "additionalProperties": false
                },
                "description": "Hard blocks: no class may overlap these windows."
              },
              "preferredFreeDays": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 7
                },
                "description": "Soft preference for days with no class."
              },
              "maxDailyHours": {
                "type": "number",
                "minimum": 0,
                "maximum": 12,
                "description": "Hard cap on class hours per day. 0 = no cap."
              },
              "preferredDayOrder": {
                "type": "string",
                "enum": [
                  "front-load",
                  "back-load",
                  "none"
                ]
              },
              "avoidBackToBackTypes": {
                "type": "array",
                "items": {
                  "type": "array",
                  "minItems": 2,
                  "maxItems": 2,
                  "items": {
                    "type": "string",
                    "enum": [
                      "LEC",
                      "TUT",
                      "LAB",
                      "SEM",
                      "DES",
                      "PRJ"
                    ]
                  }
                },
                "description": "Type pairs that may not run with zero gap, e.g. [[\"LEC\",\"LAB\"]]."
              }
            },
            "required": [],
            "additionalProperties": false
          },
          "reset": {
            "type": "boolean",
            "description": "Restore defaults before merging."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "generate_timetables",
      "title": "Generate timetables",
      "description": "Search for clash-free combinations of the modules in the plan under the given preferences (merged over the saved ones, not saved). Returns ranked options. Set apply to a rank to write that option into the plan; the result then lists exactly what was applied (the search has a time budget, so a rerun can rank options differently).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "config": {
            "type": "object",
            "properties": {
              "dayStartTime": {
                "type": "integer",
                "minimum": 700,
                "maximum": 2230,
                "description": "24-hour time as a number, e.g. 830 or 1430."
              },
              "dayEndTime": {
                "type": "integer",
                "minimum": 700,
                "maximum": 2230,
                "description": "24-hour time as a number, e.g. 830 or 1430."
              },
              "maxConsecutiveSlots": {
                "type": "integer",
                "minimum": 0,
                "maximum": 12,
                "description": "Longest run of back-to-back 30-minute slots. 0 = no limit."
              },
              "minGapBetweenClasses": {
                "type": "integer",
                "minimum": 0,
                "maximum": 4,
                "description": "Minimum gap between classes, in 30-minute units."
              },
              "lunchBreaks": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "day": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 7,
                      "description": "1 = Monday ... 7 = Sunday."
                    },
                    "startTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    },
                    "endTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    }
                  },
                  "required": [
                    "day",
                    "startTime",
                    "endTime"
                  ],
                  "additionalProperties": false
                },
                "description": "Windows to keep clear, at most one per day."
              },
              "loadPreference": {
                "type": "string",
                "enum": [
                  "compact",
                  "spread"
                ]
              },
              "skippableTypes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "LEC",
                    "TUT",
                    "LAB",
                    "SEM",
                    "DES",
                    "PRJ"
                  ]
                },
                "description": "Class types the generator may leave out of a combination."
              },
              "blacklistedTimeslots": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "day": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 7,
                      "description": "1 = Monday ... 7 = Sunday."
                    },
                    "startTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    },
                    "endTime": {
                      "type": "integer",
                      "minimum": 700,
                      "maximum": 2230,
                      "description": "24-hour time as a number, e.g. 830 or 1430."
                    }
                  },
                  "required": [
                    "day",
                    "startTime",
                    "endTime"
                  ],
                  "additionalProperties": false
                },
                "description": "Hard blocks: no class may overlap these windows."
              },
              "preferredFreeDays": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 7
                },
                "description": "Soft preference for days with no class."
              },
              "maxDailyHours": {
                "type": "number",
                "minimum": 0,
                "maximum": 12,
                "description": "Hard cap on class hours per day. 0 = no cap."
              },
              "preferredDayOrder": {
                "type": "string",
                "enum": [
                  "front-load",
                  "back-load",
                  "none"
                ]
              },
              "avoidBackToBackTypes": {
                "type": "array",
                "items": {
                  "type": "array",
                  "minItems": 2,
                  "maxItems": 2,
                  "items": {
                    "type": "string",
                    "enum": [
                      "LEC",
                      "TUT",
                      "LAB",
                      "SEM",
                      "DES",
                      "PRJ"
                    ]
                  }
                },
                "description": "Type pairs that may not run with zero gap, e.g. [[\"LEC\",\"LAB\"]]."
              }
            },
            "required": [],
            "additionalProperties": false
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          },
          "maxResults": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Default 5."
          },
          "apply": {
            "type": "integer",
            "minimum": 1,
            "description": "Rank of the option to apply to the plan. Omit to only preview."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "get_share_link",
      "title": "Share link",
      "description": "A link that opens the plan read-only on NTUMods, and a text code that can be pasted into the import box.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "load_shared_timetable",
      "title": "Load shared timetable",
      "description": "Replace a plan with a shared timetable, from a /share link or an NTUMODS1- text code. Asks the user to confirm if the plan is not empty.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "link": {
            "type": "string"
          },
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [
          "link"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "consequentialHint": true
      }
    },
    {
      "name": "export_calendar_ics",
      "title": "Export calendar",
      "description": "The plan as an iCalendar (.ics) document covering the teaching weeks, ready to save and import into Google Calendar, Apple Calendar or Outlook.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Timetable plan number, 1 to 5. Defaults to the plan being viewed."
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "list_favourites",
      "title": "List favourites",
      "description": "The modules the user has starred.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "set_favourite",
      "title": "Star or unstar module",
      "description": "Add or remove a module from the user's favourites.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{2,12}$",
            "description": "NTU module code, e.g. SC1003. Case-insensitive."
          },
          "favourite": {
            "type": "boolean"
          }
        },
        "required": [
          "code",
          "favourite"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false
      }
    },
    {
      "name": "calculate_cgpa_targets",
      "title": "CGPA planner",
      "description": "For each honours class (Highest Distinction 4.50, Distinction 4.00, Merit 3.50, Honours 3.00), the average grade needed over the remaining AU, whether it is secured, possible or out of reach, and the lowest letter grade that gets there. Unofficial; some programmes set their own bands.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "currentCgpa": {
            "type": "number",
            "minimum": 0,
            "maximum": 5
          },
          "auCompleted": {
            "type": "number",
            "minimum": 0,
            "description": "Letter-graded AU already counted in the CGPA."
          },
          "auRemaining": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 200
          }
        },
        "required": [
          "currentCgpa",
          "auCompleted",
          "auRemaining"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "project_cgpa",
      "title": "Project CGPA",
      "description": "Final CGPA and honours class if the remaining AU all earn a given letter grade.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "currentCgpa": {
            "type": "number",
            "minimum": 0,
            "maximum": 5
          },
          "auCompleted": {
            "type": "number",
            "minimum": 0
          },
          "auRemaining": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 200
          },
          "grade": {
            "type": "string",
            "enum": [
              "A",
              "A-",
              "B+",
              "B",
              "B-",
              "C+",
              "C",
              "D+",
              "D"
            ]
          }
        },
        "required": [
          "currentCgpa",
          "auCompleted",
          "auRemaining",
          "grade"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_current_module",
      "title": "Module on this page",
      "description": "On a module page, the full record of the module being viewed. Same output as get_module.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "untrustedContentHint": true
      },
      "pathPrefix": "/mods/"
    }
  ]
}