İTÜ MCP
PYPI · ITU-MCP
İTÜ Ninova, OBS, Portal, kampüs servisleri ve kütüphane için MCP sunucusu
Available components
Last scanned (26 minutes ago)
Scores higher than 73% of PyPI servers
Category Education and Research
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security50
- Malware scan not yet available for this package.Unverified
- No known CVEs affecting this package version or its production dependencies.Pass
- Runs setuptools.build_meta at install time, a recognised build step with no custom scripting around it. View diagnostics → Pass
- 0 of 37 dependencies flagged as unhealthy. View diagnostics → Pass
Provenance & Transparency100
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Cryptographically verified build provenance (signed, bound to yatuk/itu-mcp). View diagnostics → Pass
- Clear OSI-approved license (MIT).Pass
- Actively maintained (last published 8 days ago).Pass
- Publishes a security disclosure policy (SECURITY.md).Pass
Schema Quality & AI Usability75
- 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
- AI-judged instruction clarity (good).Pass
- Context-footprint check failed: tool/resource definitions use about 16781 tokens (~132/item across 127 items; 122 tools + 5 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management80
- Stability observed for 24 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage75
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 12% of tool parameters carry a description.Partial
- Structured output schemas are declared (99% of tools); any adoption earns full credit.Pass
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- An AI judge read all 124 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 5 Nov 26 −17
- Malware scan: pass → unverified ▼ security
- Stability: fail → 0.80 functional
- 31 Oct 26 +15
- Malware scan: unverified → pass ▲ security
- 27 Oct 26 0
- Package version: 1.3.0 → 1.4.0 functional
- 23 Oct 26 0
- Package version: 1.2.1 → 1.3.0 functional
- 19 Oct 26 0
- Security disclosure: unverified → pass ▲ functional
- 18 Oct 26 0
- Security disclosure: pass → unverified ▼ functional
- 15 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 92 to 95.
- 13 Oct 26 −14
- Malware scan: pass → unverified ▼ security
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 5 Nov 2026 · Analysed pypi/itu-mcp@1.4.0
Provenance Verified
A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.
| Result | Verified |
|---|---|
| Ecosystem | pypi |
| Reason | Verified |
| Discovered via | Registry attestation endpoint |
| Source repo | yatuk/itu-mcp |
| Certificate issuer | https://token.actions.githubusercontent.com |
| Certificate SAN | https://github.com/yatuk/itu-mcp/.github/workflows/publish.yml@refs/tags/v1.4.0 |
| Rekor log index | 3523641015 |
| Predicate type | PyPI publish attestation https://docs.pypi.org/attestations/publish/v1 |
| Subject digest | sha256:04cee16eb32a3e997830b216bb54586d4ff8d94c76efdcc515f4312fcc92a6fb |
Background: How many MCP packages publish verified provenance →
Install scripts 1 script
| Hook | Tier | Command |
|---|---|---|
| build_backend | allowlisted | setuptools.build_meta |
Background: Why install scripts are a supply-chain risk →
Dependencies 37 packages
| Packages resolved | 37 |
|---|---|
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
archive_compare_terms Archive: Compare Terms ~227
Diff one course's sections between two archived terms: instructor turnover, section-count delta, and capacity/fill movement, named explicitly rather than left for two separate archive_term_sections calls to be compared by eye. Read four flags before quoting a number: 'comparable'; 'coverage_differs' (the two dumps hold different study levels, listed under 'levels'; only the shared levels are compared); 'enrolment_comparison' (a side marked enrolment_not_captured has 0 enrolled in every section of the whole term: no enrolled count or fill ratio, and not a term with zero students); and 'capacity_comparison' (a side marked capacity_not_captured has 0 capacity in every section of the whole term: total_capacity and capacity_delta are null). Each block gives 'comparable', 'not_captured_terms' and a 'reason'; a side's 'data_quality' names what is missing in one line. Section counts and instructors are compared either way.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | string | yes | – |
| term_a | – | yes | – |
| term_b | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_course_catalog Archive: Course Catalog ~108
Catalog entry for a course from the archive: Turkish and English name, language, credits (theory/practice/lab/local/ECTS), description, learning outcomes, weekly topics, textbooks, and the OBS source URL. Fields the catalog does not hold are named in 'missing_fields' rather than filled in. 'coverage' separates 'course_absent_from_catalog' from 'branch_catalog_missing' (no catalog file was ever recorded for the branch).
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_course_history Archive: Course History ~301
Term-by-term offering history for a course from the archive: which terms it ran, how many sections, which instructors, and which season it opens in. Answers 'is this course ever offered in the spring?', which OBS cannot. Read seasonality.pattern before claiming a season: insufficient_data (fewer than 3 term records) means the archive cannot tell, not that the course runs in one term; summer school is counted apart from Güz and Bahar. Placeholder counts: a term in enrolment_not_captured_terms has 0 enrolled in every section of the whole term (dump taken before registration opened), so its enrolled and fill_ratio are null: unknown, not zero. A term in capacity_not_captured_terms also has 0 capacity everywhere: its capacity is null too. Such terms are left out of every average and carry a one-line data_quality. A single 0 in a captured term is real. capture_basis is 'term' when the decision rests on term-wide totals, 'branch' when the totals of the terms in capture_basis_branch_terms could not be read and only the course's branch file decided. A course whose department code changed keeps its earlier terms under the old code (CEN 411E ran as BLG 411E): same_course_other_codes lists such codes, never merged; call again with that code for the earlier history.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | string | yes | – |
| limit_terms | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_course_unlocks Archive: Course Unlocks ~141
Which courses list the given course as a prerequisite, from the archive's reverse prerequisite index. This is the direct next level only ('depth' is 'direct'), not the whole chain. Prerequisite rules have AND/OR (Ve/Veya) alternatives and minimum letter grades that this flat list does not express, so passing the course does not by itself make the listed courses takeable: call the tool named in 'rule_tool' (explain_course_eligibility) on a listed course for the real rule. 'coverage' is 'not_listed_as_prerequisite' when no course names it.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_exam_schedule Archive: Exam Schedule ~107
Final exam schedule the archive recorded for a term (date, day, time, place, exam type), optionally filtered by branch or exact course code. Most terms have no recorded schedule: 'coverage' is then 'exam_schedule_not_recorded', which means the schedule was never captured, not that there are no exams. For the live current schedule use get_public_exam_schedule.
| Name | Type | Req | Description |
|---|---|---|---|
| branch | – | – | – |
| course_code | – | – | – |
| limit | integer | – | – |
| term | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_fill_rate Archive: Fill Rate ~320
How full a section got. With a CRN, reads that term's quota time series. With a course code, reports capacity vs enrolment across every past section so you can judge whether the course reliably fills up; add a term to narrow the course answer to that one term (term_filter says which). average_fill_ratio, max_fill_ratio and always_fills are null when no section has a usable ratio. Placeholder counts: a term in enrolment_not_captured_terms has 0 enrolled in every section of the whole term (dump taken before registration opened), so its enrolled and fill_ratio are null: unknown, not zero. A term in capacity_not_captured_terms also has 0 capacity everywhere: its capacity is null too. Such terms are left out of every average and carry a one-line data_quality. A single 0 in a captured term is real. capture_basis is 'term' when the decision rests on term-wide totals, 'branch' when the totals of the terms in capture_basis_branch_terms could not be read and only the course's branch file decided. The CRN note states how old the last snapshot is and whether the archive is scanning now (scan_active); the numbers are never live. A course whose department code changed keeps its earlier terms under the old code (CEN 411E ran as BLG 411E): same_course_other_codes lists such codes, never merged; call again with that code for the earlier history.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | – | – | – |
| crn | – | – | – |
| term | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_grade_distribution Archive: Grade Distribution ~310
Letter grade counts for a course across the terms the archive has (from 2023-2024 on): per term the counts ('grades'), the published total, and the OBS source URL. Returns the most recent 6 terms by default: 'total_term_count' says how many exist, 'available_terms' names them, 'truncated' says some were left out; raise 'limit_terms' or pass 'term' for the rest. Percentages are not returned unless 'include_percentages' is true; they are shares of the counted grades ('counted_total'), so they add up to 100 even when the published total disagrees. Only the exact code is used; a Turkish/English sibling such as 'BLG 212' vs 'BLG 212E' is listed under 'related_codes', kept apart and never merged into this result, and 'same_counts_as' marks terms where the archive stores identical numbers under both codes (do not add them up). Grade labels are passed through as published, including the '+' grades; a label absent from a term was not published and is not zero. 'total_mismatch' flags terms where the published total differs from the sum of the counts; both numbers are kept. Check 'coverage': 'branch_grades_missing' means no grades file was ever recorded for the branch.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | string | yes | – |
| include_percentages | boolean | – | – |
| limit_terms | integer | – | – |
| term | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_instructor_courses Archive: Instructor Courses ~47
List the courses one instructor has taught across archived terms, newest first. Returns candidate matches when the name is ambiguous.
| Name | Type | Req | Description |
|---|---|---|---|
| instructor | string | yes | – |
| limit | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_list_branches Archive: List Branches ~77
List every branch present in one term's archive dump, with section and course counts and the study levels the dump holds ('levels', e.g. LS undergraduate, LU graduate). Answers 'does this branch even have a döküm for this term?' directly, instead of probing archive_term_sections branch by branch.
| Name | Type | Req | Description |
|---|---|---|---|
| term | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_list_terms Archive: List Terms ~195
List every term held in the İTÜ ders arşivi (2016-2017 onward), including terms OBS no longer publishes and terms not yet active. Every archive tool that takes a term accepts the slug ('2025-2026-guz'), the OBS term id (202610) or the label printed here ('2025-2026 Güz Dönemi'). The archive exists because OBS publishes only the active term: the active term comes from OBS, past terms from historical dumps. Section counts of two terms are not comparable when 'coverage_may_differ' names one of them: the live term holds graduate sections that older dumps may lack. A term carries 'enrolment_not_captured' / 'capacity_not_captured' only when the index itself holds that term's totals (today the live term); for another term call a term tool such as archive_term_sections, which reports both flags.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_search_courses Archive: Search Courses ~136
Search the archive's full course index by code or name fragment, across every term it has ever seen — not just the active one. Use this to find the exact code archive_course_history needs when only a name fragment is known. Matching ignores case, accents and spacing ('cen411e', 'yazilim'). The index holds one name per course, in its language of instruction: a course taught in English (code ending in E) is found by its English name or its code, not by a Turkish name. An empty result carries a hint saying so.
| Name | Type | Req | Description |
|---|---|---|---|
| limit | integer | – | – |
| query | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_search_sections Archive: Search Sections ~234
Search one whole term across all branches at once by course code fragment, course name fragment, instructor, and/or day; every filter given must match. Matching is case-insensitive and Turkish-aware ('sahin' finds 'Şahin'). Defaults to the archive's current term. Use this when the branch is unknown or the question spans branches; archive_term_sections reads a single branch in more detail. Returns 'match_count' and 'truncated' alongside 'coverage'. 'enrolment_not_captured' means no section of the whole term has an enrolled student in the dump (taken before registration opened): 'enrolled' and 'fill_ratio' are then null, unknown and not zero. 'capacity_not_captured' means no section of the term has a capacity either: 'capacity' is null too. 'data_quality' names what is missing in one line. A single 0 in a term that was captured is a real 0.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | – | – | – |
| course_name | – | – | – |
| day | – | – | – |
| instructor | – | – | – |
| limit | integer | – | – |
| term | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_status Archive: Status ~81
When the archive scraper last ran and last succeeded, its section count, whether the last run was partial, which branches failed, and the data age in days ('data_age_days'). Informational only: the archive deliberately stops scraping outside registration and add/drop weeks, so an old date or a large data_age_days is expected and is not an error or a fault.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_term_sections Archive: Term Sections ~167
List archived sections for a term, filtered by branch, course code, or instructor. Critically, the archive holds terms OBS has not published yet, so upcoming-term offerings can be checked instead of guessed. A null instructor means none was announced. enrolment_not_captured means no section of the whole term has an enrolled student in the dump (taken before registration opened): enrolled and fill_ratio are null, unknown and not zero. capacity_not_captured means no section of the term has a capacity either: capacity is null too. data_quality names what is missing in one line. A single 0 in a captured term is a real 0.
| Name | Type | Req | Description |
|---|---|---|---|
| branch | – | – | – |
| course_code | – | – | – |
| instructor | – | – | – |
| limit | integer | – | – |
| term | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
archive_who_taught Archive: Who Taught ~284
List every instructor who has taught a course across archived terms, ranked by how many terms they taught it, with their most recent term and average section fill rate. Sections with no announced instructor are counted in unannounced_section_count, never listed as a person. fill_ratio_section_count says how many sections an average rests on; average_fill_ratio is null when it rests on none. Placeholder counts: a term in enrolment_not_captured_terms has 0 enrolled in every section of the whole term (dump taken before registration opened), so its enrolled and fill_ratio are null: unknown, not zero. A term in capacity_not_captured_terms also has 0 capacity everywhere: its capacity is null too. Such terms are left out of every average and carry a one-line data_quality. A single 0 in a captured term is real. capture_basis is 'term' when the decision rests on term-wide totals, 'branch' when the totals of the terms in capture_basis_branch_terms could not be read and only the course's branch file decided. A course whose department code changed keeps its earlier terms under the old code (CEN 411E ran as BLG 411E): same_course_other_codes lists such codes, never merged; call again with that code for the earlier history.
| Name | Type | Req | Description |
|---|---|---|---|
| course_code | string | yes | – |
| limit_terms | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
auth_status Authentication Status ~24
Check whether Ninova credentials are configured and whether a fresh session can be created.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
build_degree_plan Build Degree Plan ~115
Read an official semester-by-semester OBS curriculum; latest plan is selected by default. Courses are listed once, under plan.semesters[].courses. 'ects' is a number; when the plan gives a range such as '4 / 5' it is null and ects_min, ects_max and ects_raw carry the range.
| Name | Type | Req | Description |
|---|---|---|---|
| faculty_id | integer | yes | – |
| latest | boolean | – | – |
| plan_id | – | – | – |
| plan_type | string | – | – |
| program_code | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
calculate_target_gpa Calculate Target GPA ~296
Quick arithmetic without the student's record and without login: the average needed over future credits to reach a target cumulative GPA, from four numbers the caller supplies. It cannot see retakes; a retake replaces its old grade and adds no credit, so state them in retakes or the result understates what is reachable (see the assumption field). When the student is logged in, prefer obs_simulate_cumulative_gpa(target_gpa=...), which works from the record and handles retakes.
| Name | Type | Req | Description |
|---|---|---|---|
| current_credits | number | yes | Credits that count in the current GPA, e.g. 101. Not ECTS and not the credits completed. |
| current_gpa | number | yes | Current cumulative GPA (GANO) on the 4.00 scale, e.g. 2.35. |
| future_credits | number | yes | Total credits of the courses still to be taken, retakes included, e.g. 18. |
| retakes | – | – | The part of future_credits that repeats a course already counted in the current GPA, e.g. [{"credit": 3, "old_grade": "FF"}, {"credit": 4, "old_grade": "DD"}]. A retake replaces its old grade and add… |
| target_gpa | number | yes | Cumulative GPA to reach, e.g. 2.5. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
check_course_conflicts Check Course Conflicts ~126
Check for schedule time conflicts between courses by CRN. Uses the public course schedule to look up session day/time/room. Give only the CRNs: the department of each CRN is found from the course archive (no login) and, for what is left, from the student's registered courses when logged in. Read status: ok, partial (missing_crns were not checked), not_found, or departments_required (pass department_codes).
| Name | Type | Req | Description |
|---|---|---|---|
| crns | array | yes | – |
| department_code | – | – | – |
| department_codes | – | – | – |
| program_type | string | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
check_watched_course_sections Check Watched Course Sections ~225
Read the public OBS schedule now for the watched sections (one request per branch, no login) and report per section: capacity, enrolled, seats free, and what changed since the previous check ('opened', 'filled', 'capacity_raised', 'delisted', ...). The first check of a section only sets a baseline. Status 'unknown' means OBS gave no usable numbers (missing values, or the enrolled column not populated yet) and is never a free seat; 'not_listed' means the CRN is absent from the schedule, which is not the same as full. Entries from a past term are reported as 'stale_term' and not compared. This is a one-shot check: the server cannot run in the background and sends no notifications, so a periodic check must be driven by the user or by a scheduled task on the client. Schedule reads are cached for a short time; when 'freshness.all_from_cache' is true the numbers were not re-read, so wait 'retry_after_seconds' instead of calling again in a loop.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
crawl_course Crawl Course ~72
Inventory pages and downloadable resources inside a Ninova course tree. A link that is refused or fails is listed in skipped with its reason and the crawl continues.
| Name | Type | Req | Description |
|---|---|---|---|
| course_url | string | yes | – |
| include_downloads | boolean | – | – |
| max_depth | integer | – | – |
| max_pages | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
diff_snapshot Diff Snapshot ~41
Compare the current state of a Ninova page against a previously stored snapshot.
| Name | Type | Req | Description |
|---|---|---|---|
| label | – | – | – |
| snapshot_path | – | – | – |
| url | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
download_resource Download Resource ~64
Download a Ninova file or other authenticated resource to disk. The text of a saved PDF/DOCX/PPTX/TXT is also added to the local index that search_content searches.
| Name | Type | Req | Description |
|---|---|---|---|
| filename | – | – | – |
| output_dir | – | – | – |
| url | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
estimate_relative_grade Estimate Relative Grade ~257
Estimate a likely letter grade from raw class scores under İTÜ's relative-grading (bağıl değerlendirme) rules, using both official methods: Method 1 (T-score against Table 1, an EXAMPLE class-level table from the regulation — the actual instructor may use different T-score cutoffs) and Method 2 (mean ± standard-deviation multiples, Table 2, a fixed formula with no table lookup). This is an estimate for planning purposes only, not the official grade. Scores must be finite numbers from 0 to 100; anything else is rejected with the offending value. No letter is estimated when fewer than 10 scores are counted or when all scores are equal: estimate_available is false, no_estimate_reason says why, and the mean and standard deviation are still returned. my_score is added to the list unless the same value is already in it (reported as my_score_included). class_scores must be every student's raw 0-100 score who counts toward the relative-grading average (VF students are excluded from that average per the regulation and must not be included here).
| Name | Type | Req | Description |
|---|---|---|---|
| class_scores | array | yes | – |
| my_score | number | yes | – |
| my_score_included | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
explain_course_eligibility Explain Course Eligibility ~402
Explain whether a course's prerequisites are satisfied, using the official OBS branch prerequisite table (full Ve/Veya expression, per-course minimum grades, and credit requirement). Supports 3- and 4-digit codes including capstone courses like CEN 4901E. 'prerequisite_status' distinguishes 'no_prerequisites' (proven absent from the official table) from 'unknown' (table unreadable) — never read an empty result as 'no prerequisites'. 'cross_check' additionally diffs the result against an independent community dataset (third-party, not official); OBS stays authoritative and a disagreement is reported, never silently resolved either way. 'archive_seasonality', when the archive has the course, flags courses that only open in one term of the year (e.g. eligible now, but only ever offered in Güz) — check it even when eligibility alone looks fine. With use_obs_history=true, prerequisites the student is taking this term are listed in in_progress_prerequisites and eligible_if_in_progress_passed says what the answer becomes once they are passed; report that instead of a bare 'not eligible'. In missing_courses the alternatives of one OR group are a single entry ('A VEYA B'): one of them is enough. requirement is the whole rule as text with its minimum grades, and prerequisite_grades holds the student's grade in each prerequisite course already completed. The full completed-course history and the rule tree are returned only with include_raw=true.
| Name | Type | Req | Description |
|---|---|---|---|
| class_year | – | – | – |
| completed_courses | – | – | – |
| completed_credits | – | – | – |
| course_code | string | yes | – |
| include_raw | boolean | – | Also return the whole completed-course history (completed_courses, completed_grades), requirement_tree, minimum_grades and explanation_parts. Default false: the verdict, the requirement as text and t… |
| program_type | string | – | – |
| use_obs_history | boolean | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
export_course_schedule_ics Export Course Schedule (.ics) ~239
Build an iCalendar (.ics) document from the weekly meetings of the given CRNs, read from the public OBS course schedule (no login). Each meeting becomes a weekly recurring event in Europe/Istanbul time between the first and last day of classes. Those two dates are taken from term_start/term_end; when omitted they are read from the official academic calendar ("Beginning/End of Fall Term" for that academic year and level) and 'term_date_sources' says where each came from. If the calendar does not state one, the result is status='term_dates_required' with no calendar: ask the user for the dates instead of guessing. Meetings whose day or time is a placeholder ('-', '--') are listed in 'skipped' and never given a time; CRNs not found in the scanned departments are in 'missing_crns'. Holidays are not excluded from the recurrence. Returns the text in 'ics'; it does not write a file.
| Name | Type | Req | Description |
|---|---|---|---|
| crns | array | yes | – |
| department_codes | array | yes | – |
| program_type | string | – | – |
| term_end | – | – | – |
| term_start | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
export_exam_schedule_ics Export Exam Schedule (.ics) ~148
Build an iCalendar (.ics) document from the current public OBS final exam schedule (no login), filtered to the given course codes. Each exam is a single dated event in Europe/Istanbul time. An exam whose date or time is missing or unreadable is listed in 'skipped'; a code with no row is in 'not_found'; a branch whose schedule is not published yet is skipped with 'exam_schedule_not_published', which is not the same as having no exam. When only a start time is published the event has no end time ('end_time_unknown'). Returns the text in 'ics'; it does not write a file.
| Name | Type | Req | Description |
|---|---|---|---|
| course_codes | array | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
export_table Export Table (CSV) ~461
Export one of the student's own tables as a CSV that opens correctly in a Turkish Excel: UTF-8 with a byte order mark, ';' as the delimiter, decimal comma, Turkish column headers. Use it when the user wants their grades, weekly schedule or assignment deadlines as a spreadsheet or a file. Returns 'columns', 'row_count' and the text in 'csv' (or 'path' with save=true). A text of more than about 200 KB is cut at a row boundary with 'truncated' and 'total_row_count'. Cells that a spreadsheet would run as a formula are prefixed with a single quote. term_grades also reports the term average; deadlines is read from the stored snapshot without a sync and reports 'stale' and 'snapshot_age_hours', so call sync_all_courses first when it is stale. Reads only; nothing is changed at the university.
| Name | Type | Req | Description |
|---|---|---|---|
| dataset | string | yes | Which table to export. term_grades: the courses of one term (Ders Kodu, Ders Adı, Kredi, Harf Notu). all_grades: every graded attempt of every term (Dönem, Ders Kodu, Ders Adı, Kredi, Harf Notu, Duru… |
| save | boolean | – | false (default): return the CSV text in 'csv'. true: write the file into the exports folder of the server's downloads directory and return its path instead of the text. An existing file is never repl… |
| semester | – | – | Term for term_grades and weekly_schedule: term code, OBS term id or name, e.g. "202610", 202610 or "2025-2026 Bahar". Default: the latest term. Ignored for all_grades and deadlines. |
Structured output declared, but exposes no named fields.
No examples provided.
find_empty_classrooms Find Empty Classrooms ~77
Estimate empty rooms at a weekday/time from selected public department schedules. Only rooms that appear in the scanned schedules are known; sessions without an announced building or room are ignored.
| Name | Type | Req | Description |
|---|---|---|---|
| building | – | – | – |
| day | string | yes | – |
| department_codes | array | yes | – |
| program_type | string | – | – |
| time | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
find_open_course_sections Find Open Course Sections ~188
Find sections with seats a student can actually take, across selected public OBS department schedules. Part of a section's capacity can be reserved for named programmes, so 'available_seats' counts only the general pool; pass program_code to add your own programme's remaining quota. 'total_free_seats' is capacity minus enrolled and 'reserved_free_seats' lists free seats held for other programmes. Sections without a day and time (project and graduation work) are listed last with unscheduled=true; 'Ek Sınav' rows are left out. An empty result carries 'no_result_reason': the query matched no section, the matching sections are full, or their free seats are reserved for other programmes.
| Name | Type | Req | Description |
|---|---|---|---|
| department_codes | array | yes | – |
| min_available_seats | integer | – | – |
| program_code | – | – | – |
| program_type | string | – | – |
| query | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_academic_calendar Academic Calendar ~308
Read the official İTÜ academic calendar (public, no login): term start and end, course registration, add-drop and withdrawal periods, final and make-up exams, breaks. Covers the whole current academic year, not only this month; with date_from/date_to the academic years those dates fall in are read (at most 2 per call, and only years the site still lists). Each event has an English 'description' and a Turkish 'description_tr'; query matches either language, ignoring case and Turkish accents (e.g. 'dersten çekilme', 'ekle bırak', 'final'). 'terms' gives the first and last day of classes per term. With no argument only recent and upcoming events are listed (see 'default_window'). Check 'coverage' and 'coverage_warning' before concluding that nothing is scheduled. Official public holidays are not on this calendar. An event carries only what is its own: an empty field is left out (no start_at means the source gives no time of day), and timezone, source_url and academic_year are on an event only when they differ from the result's own timezone, url and academic_year.
| Name | Type | Req | Description |
|---|---|---|---|
| category | – | – | – |
| date_from | – | – | – |
| date_to | – | – | – |
| include_raw | boolean | – | Return every event with all its fields, including empty ones, the site's row id (uid) and the timezone, source_url and academic_year repeated on each event. Default false. |
| query | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_assignment_upload_slots Get Assignment Upload Slots ~59
List the file slots on an assignment's 'Ödevi Yükle / OdevGonder' page, including which slots are already filled and allowed extensions.
| Name | Type | Req | Description |
|---|---|---|---|
| assignment | – | – | – |
| course | – | – | – |
| upload_url | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_cafeteria_menu Cafeteria Menu ~53
Read a dated lunch/dinner or vegan menu from the İTÜ Portal (requires login), including calories and allergens.
| Name | Type | Req | Description |
|---|---|---|---|
| date | – | – | – |
| meal | – | – | – |
| vegan | boolean | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_announcements Get Course Announcements ~47
Return announcements for a specific Ninova course.
| Name | Type | Req | Description |
|---|---|---|---|
| compact | boolean | – | – |
| course | – | yes | – |
| include_full_text | boolean | – | – |
| limit | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_assignments Get Course Assignments ~66
Return a course's assignment list. By default also fetches each assignment detail/upload page; set include_details=false for a faster list-only read.
| Name | Type | Req | Description |
|---|---|---|---|
| compact | boolean | – | – |
| course | – | yes | – |
| include_details | boolean | – | – |
| limit | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_attendance Get Course Attendance ~146
Read the Ninova 'Yoklama' page for a course — attendance as the instructor recorded it in the LMS, not the official OBS record. The grid shows 0 both for an absence and for a session not entered yet, so only zeros in weeks that also hold a recorded 1 are counted as absences (total_absent_marks); the rest are total_unmarked_or_absent_marks, and weeks after today are total_future_marks. Never report unmarked or future marks as absences; when absence_counts_reliable is false, say the count is uncertain. For official attendance with an absence-risk summary, use obs_get_attendance instead.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_class_files Get Course Class Files ~58
List files and folders under the Ninova 'Sınıf Dosyaları' section for a course.
| Name | Type | Req | Description |
|---|---|---|---|
| compact | boolean | – | – |
| course | – | yes | – |
| max_depth | integer | – | – |
| recursive | boolean | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_grades Get Course Grades ~61
Read the Ninova 'Notlar' page for a course — grades as the instructor entered them in the LMS, not the official transcript record. For the official letter/midterm grade, use obs_get_course_grades instead.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_info Get Course Info ~31
Return structured information from a course's 'Sınıf Bilgileri' page.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_lesson_files Get Course Lesson Files ~58
List files and folders under the Ninova 'Ders Dosyaları' section for a course.
| Name | Type | Req | Description |
|---|---|---|---|
| compact | boolean | – | – |
| course | – | yes | – |
| max_depth | integer | – | – |
| recursive | boolean | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_message_board Get Course Message Board ~46
Read the Ninova 'Mesaj Panosu' page for a course.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | yes | – |
| include_thread_details | boolean | – | – |
| limit | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_overview Get Course Overview ~151
Return a combined view of a course's sections, announcements, assignments, files, grades, message board, attendance, and remote learning routes. Without refresh=true a course that was synced before is answered from the stored snapshot (source: tracked_state). The result carries snapshot_age_hours and stale: stale=true means the stored data is older than the limit (12 hours by default, NINOVA_TRACKING_MAX_AGE_SECONDS) and must not be presented as the current state. Call again with refresh=true for live data.
| Name | Type | Req | Description |
|---|---|---|---|
| compact | boolean | – | – |
| course | – | yes | – |
| file_max_depth | integer | – | – |
| include_assignment_details | boolean | – | – |
| refresh | boolean | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_remote_learning Get Course Remote Learning ~29
Read the Ninova 'Uzaktan Eğitim' page for a course.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_course_sections Get Course Sections ~28
List the direct course routes exposed on the Ninova course home page.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_dashboard Get Dashboard ~175
Read the Ninova dashboard and summarize courses, recent announcements, assignments, and messages. Each course row is one enrolled section: section and crn tell two sections of one course apart, and code_uncertain marks a code that could not be read reliably. Dashboard dates have no year: date_iso is inferred from today's date (date_year_inferred). A start/end window is split in date_ranges (start, end, raw) without ISO values. The page's link list and headings are left out by default (link_count says how many links there are); include_raw=true adds them.
| Name | Type | Req | Description |
|---|---|---|---|
| compact | boolean | – | – |
| include_raw | boolean | – | Also return links (every link of the page, mostly site navigation) and page.headings. Default false: link_count only. With compact=true the list is still shortened. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_dashboard_announcements Get Dashboard Announcements ~40
Return the announcements listed under the Ninova dashboard's aggregated announcements page.
| Name | Type | Req | Description |
|---|---|---|---|
| include_full_text | boolean | – | – |
| limit | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_dashboard_assignments Get Dashboard Assignments ~50
Return the assignments listed under the Ninova dashboard's aggregated assignments page. Set include_details=false for a faster list-only read.
| Name | Type | Req | Description |
|---|---|---|---|
| include_details | boolean | – | – |
| limit | integer | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_help_ticket Help Ticket Detail ~90
Read one help ticket by numeric ID: title, description, unit, category, subcategory, status, dates, messages, institutional replies and attachment links. Does not download attachments. Reply detection compares history authors with the original requester. Missing fields are null. Dates are local to the source with no assumed timezone. Requires login.
| Name | Type | Req | Description |
|---|---|---|---|
| include_sensitive | boolean | – | – |
| ticket_id | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_itu_announcements İTÜ Announcements ~157
Aggregate official announcements from İTÜ (www.itu.edu.tr), ÖDEK, İKM, SKS and Erasmus sources, newest first across sources. 'published_at' is the source's own text and 'published_at_iso' the same date in ISO form; undated items come last. When limit is smaller than what the sources offer, every source first gets an equal share (see selection_note); ask for one source to read it in full. 'empty_sources' names sources that answered with no items, 'source_errors' those that failed. A summary cut short ends with '…' and has summary_truncated=true.
| Name | Type | Req | Description |
|---|---|---|---|
| limit | integer | – | – |
| query | – | – | – |
| sources | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
get_personal_exam_calendar Personal Final Exam Calendar ~99
Read the signed-in student's official OBS final calendar. matched_courses names the registered courses the calendar was read for (all of them, or the ones matching course) by code, CRN and title.
| Name | Type | Req | Description |
|---|---|---|---|
| course | – | – | – |
| include_raw | boolean | – | Return matched_courses as full OBS rows and the final calendar unreduced. Default false: a matched course is named by code, CRN and title only. |
| semester | – | – | – |
| Name | Type | Req | Description |
|---|---|---|---|
| result | object | yes | – |
No examples provided.
How do I install the İTÜ MCP server?
İTÜ MCP runs locally as a PyPI package, launched with uvx itu-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
pypi · itu-mcp
claude mcp add yatuk-itu-mcp -- uvx itu-mcp
What is the İTÜ MCP server?
İTÜ MCP is listed in the public MCP registry as io.github.yatuk/itu-mcp. İTÜ Ninova, OBS, Portal, kampüs servisleri ve kütüphane için MCP sunucusu. This page covers its PyPI package (itu-mcp).
Is the İTÜ MCP server safe to use?
İTÜ MCP scores 76 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 5 November 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the İTÜ MCP server expose?
İTÜ MCP exposes 122 tools: auth_status, mail_status, mail_list_inbox, mail_list_sent, mail_get_message, and 117 more. Their descriptions and schemas cost roughly 15,757 tokens of context every time the server is loaded.
Is the İTÜ MCP server still maintained?
İTÜ MCP is still listed as active in the MCP registry. We last reached this channel on 5 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What licence is the İTÜ MCP server under?
İTÜ MCP declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.