pddikti
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pddiktifind universities in Yogyakarta"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
PDDikti API & MCP Server
Open-source PDDikti API and PDDikti MCP server for Indonesian higher-education data from PDDikti (Pangkalan Data Pendidikan Tinggi): universities, study programs, lecturers, students, and aggregate statistics.
REST API with an OpenAPI 3.1 spec, Swagger UI, and ReDoc.
MCP server (Model Context Protocol) over stdio and Streamable HTTP, for Claude, Cursor, and other AI agents.
Web explorer to search a university and browse its profile.
Built with Bun, Hono, and Zod. Upstream Cloudflare clearance is automatic through FlareSolverr.
Bahasa Indonesia: API PDDikti dan MCP PDDikti tidak resmi dan open source. Dipakai untuk cek data perguruan tinggi, program studi, dosen, dan mahasiswa dari sistem PDDikti (dulu Forlap Dikti) lewat REST API, OpenAPI, atau AI agent. Lihat Ringkasan dalam Bahasa Indonesia.
Features
Typed REST API under
/api/*, validated with Zod, with Swagger UI and ReDoc.MCP server over stdio and Streamable HTTP, so AI agents can query the same data.
Web explorer: search a university and browse its profile, study programs, lecturers, and students.
Hands-off upstream access: a FlareSolverr sidecar refreshes the Cloudflare cookie on a
403. A password-protected config page is the manual fallback.Optional Redis cache and optional API key for the REST surface.
One-command deployment with Docker Compose.
Related MCP server: BPS MCP Server
Contents
Quick start
Docker Compose (recommended)
Runs the API, a FlareSolverr sidecar, and Redis.
git clone https://github.com/andikadevs/pddikti.git
cd pddikti
cp .env.example .env # set UPSTREAM_BASE_URL and REDIS_PASSWORD
docker compose up -dThe API container listens on port 3000 inside the Compose network and does not publish a
host port. Route your reverse proxy to api:3000, or add a ports entry for local use.
FlareSolverr and Redis stay internal.
Local (Bun)
Requires Bun 1.3 or newer.
bun install
cp .env.example .env # set UPSTREAM_BASE_URL
bun run devOpen http://localhost:3000.
Single container
Without the solver, refresh the Cloudflare cookie by hand on the config page.
docker build -t pddikti .
docker run -p 3000:3000 --env-file .env pddiktiConfiguration
The service reads the environment once, in src/config.ts. Bun loads .env automatically.
See .env.example for every variable.
Variable | Default | Purpose |
|
| Listen port. |
|
| Absolute base URL for docs links and |
| none | Upstream base, |
|
| Timeout per upstream request. |
| empty (off) | Protects |
|
| Lifetime of the SPA session and config cookies. |
| empty (off) | Enables the |
|
| File for runtime overrides and the solved cookie. |
|
| Solve Cloudflare challenges with FlareSolverr on a |
| none | FlareSolverr |
|
| Optional Redis response cache. |
|
| Attribution added to every JSON response. |
For a public deployment, put a TLS-terminating reverse proxy in front, setPUBLIC_BASE_URL to the HTTPS URL, and set strong values for API_KEY and
CONFIG_PASSWORD. See SECURITY.md for the full checklist.
Usage
REST API
Path | Description |
| Overview and service status |
| Swagger UI |
| ReDoc |
| Live OpenAPI document |
A static copy of the spec lives in openapi.json for offline use.
When API_KEY is set, send it on every data request:
curl -H "X-API-Key: $API_KEY" http://localhost:3000/api/search/all/telkom/Every JSON response includes a credit field. Upstream errors keep their HTTP status.
Web explorer
GET / serves a React app built with shadcn/ui on Base UI. Search a university to open
its profile at /pt/<id>: headline statistics, then tabs for the overview (identity,
costs, study duration, name history), study programs, all lecturers, and a student
search. Light and dark themes are supported.
MCP server
The MCP server exposes about ten tools, such as search_pddikti, get_university,
get_lecturer, and get_statistics. The tools use the same upstream client, cache, and
credit envelope as the REST API.
Transport | How to run | Use for |
stdio |
| Local clients |
Streamable HTTP |
| Remote clients |
The tool catalog is at GET /mcp/tools (JSON) and /mcp/docs (readable view).
/mcp is always public, even when API_KEY is set. Browser connectors cannot send a
static header, so only the /api/* REST surface is key-protected.
MCP clients
Claude Code
# Local, over stdio
claude mcp add pddikti -- bun run /abs/path/to/pddikti/src/mcp/stdio.ts
# Remote, over HTTP
claude mcp add --transport http pddikti https://<host>/mcpThe repository also includes a project-scoped .mcp.json.
Claude Desktop and other stdio clients
Add the server to the client's config file, for example claude_desktop_config.json:
{
"mcpServers": {
"pddikti": {
"command": "bun",
"args": ["run", "/abs/path/to/pddikti/src/mcp/stdio.ts"],
"env": {
"UPSTREAM_BASE_URL": "https://pddikti.kemdiktisaintek.go.id/api",
"CONFIG_STORE_PATH": "/abs/path/to/pddikti/runtime-config.json"
}
}
}
}Point CONFIG_STORE_PATH at the file the config page writes, so the stdio server reuses
the same Cloudflare cookie. You can also set SOLVER_ENABLED and SOLVER_URL.
Claude.ai and other web clients
Add a custom connector with the URL https://<host>/mcp.
Test the server
bun run mcp:inspect # MCP Inspector against the stdio server
curl -s https://<host>/mcp -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'Upstream access
The PDDikti backend is protected by Cloudflare bot management. Each request needs a
cf_clearance cookie and the exact User-Agent it was issued to. Without them, the
upstream returns 403. Without UPSTREAM_BASE_URL, /api/* returns 503.
You supply the cookie at runtime, not in .env:
Automatic. Set
SOLVER_ENABLED=trueandSOLVER_URL. On a403, the service asks FlareSolverr to solve the challenge, saves the new cookie toCONFIG_STORE_PATH, and retries once. FlareSolverr runs its own headless browser, so this works on a server with no display.Manual. Set
CONFIG_PASSWORDand open/config. Paste the cookie and User-Agent, or a full "Copy as cURL" from your browser's DevTools. Changes apply without a restart and persist across restarts.
Development
Command | Purpose |
| Dev server; reloads the server and browser on changes |
| Production server |
| MCP server over stdio |
| MCP server over stdio with hot reload |
| MCP Inspector against the stdio server |
| Regenerate the static |
| Type-check with |
| Run the test suite (no network access) |
A change is done when bun run typecheck is clean and bun test is green.
docs/ARCHITECTURE.md: design, request flow, and source layout.
CLAUDE.md: detailed coding conventions.
CHANGELOG.md: release history.
Ringkasan dalam Bahasa Indonesia
Proyek ini menyediakan API PDDikti dan MCP server PDDikti yang bisa kamu jalankan sendiri. Datanya berasal dari PDDikti (Pangkalan Data Pendidikan Tinggi) milik Kemdiktisaintek, sistem yang dulu dikenal sebagai Forlap Dikti.
Yang bisa kamu lakukan:
Cek data perguruan tinggi: profil kampus, akreditasi, status, lokasi, biaya kuliah, dan riwayat nama.
Cek program studi (prodi): daftar prodi, jenjang, akreditasi, dan rasio dosen mahasiswa.
Cek data dosen: pencarian dosen dan daftar dosen per kampus atau prodi.
Cek data mahasiswa: pencarian mahasiswa berdasarkan nama atau NIM.
Statistik pendidikan tinggi: jumlah kampus, prodi, dosen, dan mahasiswa.
Cara pakai:
Kebutuhan | Pakai |
Integrasi ke aplikasi atau backend | REST API |
AI agent (Claude, Cursor, dll.) | MCP server: |
Cari kampus lewat browser | Web explorer di |
Mulai dari Quick start. Proyek ini tidak berafiliasi dengan PDDikti atau Kemdiktisaintek.
FAQ
Is this the official PDDikti API?
No. PDDikti does not publish a public API. This project is an unofficial, self-hosted proxy in front of the data that the PDDikti website uses. It is not affiliated with PDDikti or Kemdiktisaintek.
Apakah ini API PDDikti resmi?
Bukan. PDDikti tidak menyediakan API publik. Proyek ini adalah proxy tidak resmi yang kamu host sendiri, di depan data yang dipakai situs PDDikti.
What data can I get?
Universities (perguruan tinggi), study programs (program studi), lecturers (dosen),
students (mahasiswa), and aggregate statistics. See openapi.json for
every endpoint.
How do I use PDDikti with an AI agent?
Run the MCP server and add it to your client. See MCP clients for Claude Code, Claude Desktop, claude.ai, and other MCP clients.
Is there a hosted version?
No. Deploy your own instance with Docker Compose or Bun. See Quick start.
Bagaimana cara cek data mahasiswa atau dosen di PDDikti lewat API?
Panggil endpoint pencarian, misalnya GET /api/search/mhs/<nama-atau-nim>/ untuk mahasiswa
atau GET /api/search/dosen/<nama>/ untuk dosen. Daftar lengkap ada di /api/docs.
Author
Built and maintained by Andika Dwi Saputra. See more projects and get in touch at andikadevs.space.
Contributing
Contributions are welcome. Read CONTRIBUTING.md and the Code of Conduct first. Report security problems privately, as described in SECURITY.md.
License
Copyright © 2026 Andika Dwi Saputra.
Licensed under the GNU Affero General Public License v3.0 or later. You may use, study, modify, and share this software. If you run a modified version as a network service, you must offer its complete source code to the service's users under the same license.
Disclaimer
This is a personal, non-commercial project for education and learning.
Not affiliated with or endorsed by PDDikti, Kemdiktisaintek, or Cloudflare. All upstream data is © PDDikti. Other names belong to their owners.
No warranty. The software is provided "as is", with no guarantee of accuracy, availability, or fitness for any purpose. See the license.
No liability for misuse. The author is not responsible for how this software is used or for any damage that results. You use it at your own risk.
Your responsibility. Follow all applicable laws and the terms of service of every system you reach, including the upstream's terms and its anti-bot and rate-limit protections. Do not use this software to overload, disrupt, or mass-scrape any service, or to process personal data unlawfully.
Available Tools
10 toolsget_lecturerGet lecturer infoBRead-onlyIdempotent
Fetch one aspect of a single lecturer (dosen) as JSON: profile, teaching, research, or other records. Lecturer aspect. "profile" — Get lecturer profile; "study-history" — Get lecturer education history; "teaching-history" — Get lecturer teaching history; "penelitian" — Get lecturer research portfolio; "pengabdian" — Get lecturer service portfolio; "karya" — Get lecturer works portfolio; "paten" — Get lecturer patents portfolio.
| Name | Required | Description | Default |
|---|---|---|---|
| aspect | Yes | ||
| id_dosen | Yes | Opaque lecturer (dosen) id, copied exactly from a search_pddikti result (entity "dosen"). Do not guess or construct it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive and openWorld, so the safety profile is covered. The description adds only that the result is JSON scoped to one aspect; it says nothing about missing-record behavior, error cases, or payload shape for a network-backed lookup.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The opening sentence is well front-loaded and the aspect list is useful, but the fragment "Lecturer aspect." is filler and several list entries restate the enum tautologically ("teaching-history — Get lecturer teaching history"). Roughly a third of the text adds no information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With two required params and full annotation coverage, the definition is callable as written, but with no output schema the description never indicates what an aspect payload contains (fields, pagination for portfolio aspects like karya/paten). An agent can invoke it but cannot anticipate result structure.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%: id_dosen is documented in the schema, but the aspect enum has bare values. The description compensates well by glossing each enum member in English (penelitian = research portfolio, pengabdian = service portfolio, karya, paten), which is real added meaning over the raw enum.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ("Fetch one aspect of a single lecturer") and enumerates the seven aspects, so the agent knows exactly what it returns. It does not explicitly distinguish itself from siblings like get_university_lecturers or get_study_program_lecturers, which is the only missing piece.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: fetch a single lecturer's detail by aspect, as opposed to the list-style siblings. There is no explicit when-to-use/when-not guidance and no mention that id_dosen must come from a prior search_pddikti call (that constraint lives only in the schema).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_statisticsGet aggregate statisticsARead-onlyIdempotent
Fetch one national aggregate statistic for Indonesian higher education as JSON. Needs no id; use it for country-wide counts, not for a single institution. Statistic metric. "mhs-count" — Get total student count; "mhs-count-active" — Get active student count; "mhs-count-gender" — Get student count by gender; "mhs-count-bidang-ilmu" — Get student count by field; "mhs-count-status" — Get student count by status; "mhs-count-jenjang" — Get student count by degree level; "mhs-count-kelompok-lembaga" — Get student count by institution group; "dosen-count" — Get total lecturer count; "dosen-count-active" — Get active lecturer count; "dosen-count-gender" — Get lecturer count by gender; "dosen-count-bidang" — Get lecturer count by field; "dosen-count-keaktifan" — Get lecturer count by activity; "dosen-count-jenjang" — Get lecturer count by degree; "dosen-count-ikatan" — Get lecturer count by affiliation; "pt-count" — Get total university count; "pt-count-province" — Get university count by province; "pt-count-kelompok-pembina" — Get university count by supervising group; "pt-count-akreditasi" — Get university count by accreditation; "pt-count-bentuk-pt" — Get university count by institution type; "prodi-count" — Get total study program count; "prodi-count-bidang-ilmu-terbanyak" — Get most common study program field; "prodi-count-kelompok-pembina" — Get study program count by supervising group; "prodi-count-bidang-ilmu" — Get study program count by field; "prodi-count-akreditasi" — Get study program count by accreditation; "prodi-count-jenjang" — Get study program count by degree level.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false and openWorldHint=true, so safety and repeatability are covered. The description adds that the response is JSON and that no identifier is needed, but says nothing about how the shape differs per metric or about size/latency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and scoping constraints are front-loaded, and the long enum mapping is genuinely necessary given the unreadable metric codes. Minor waste remains in the dangling fragment 'Statistic metric.' and the repetition of 'Get ... count' phrasing across entries.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description should say more about what each metric returns — for example whether 'gender' or 'province' metrics yield multi-row breakdowns versus scalars. 'As JSON' alone leaves an agent unable to anticipate the response structure for grouped metrics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the enum values are opaque Indonesian abbreviations, so the description carries the full burden. It supplies an English gloss for every one of the 25 metrics, which is real added meaning, though a few glosses are terse and don't reveal what the output looks like.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource ('Fetch one national aggregate statistic for Indonesian higher education as JSON') and pins the scope by ruling out single-institution queries. This clearly separates it from siblings like get_university and get_study_program, which operate on a specific id.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use it ('country-wide counts') and when not ('not for a single institution'), and notes no id is required. It stops short of naming the specific sibling tools to use for institution-level data, so the routing hint is directional rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_studentGet student detailARead-onlyIdempotent
Fetch the public PDDikti record of one student (mahasiswa), such as university, study program, and enrollment status. There is no student list; find the id with search_pddikti (entity "mhs") by name or NIM. Returns JSON.
| Name | Required | Description | Default |
|---|---|---|---|
| id_mhs | Yes | Opaque student (mahasiswa) id, copied exactly from a search_pddikti result (entity "mhs"). Do not guess or construct it. |
TDQS
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 useful context beyond them: that the record is public, that no listing exists, and that the return is JSON. It stops short of describing response shape or failure modes for an invalid/stale id.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the verb and resource, then the constraint, then the id-sourcing instruction. Every sentence carries load and none repeats the title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a single required parameter, annotations covering the safety profile, and no output schema, the definition is nearly complete. The description hints at returned fields ('university, study program, enrollment status') rather than documenting them fully, which is the only remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema itself already says the id is opaque and must not be guessed. The description reinforces provenance ('find the id with search_pddikti') but adds no new syntax or format detail. Baseline 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch the public PDDikti record of one student'). It also enumerates the content (university, study program, enrollment status) and explicitly distinguishes itself from list-style siblings with 'There is no student list', so an agent can tell it apart without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: to obtain an id, use search_pddikti with entity "mhs" by name or NIM. Both the when-to-use condition and the required upstream step are stated, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_study_programGet study program infoARead-onlyIdempotent
Fetch one aspect of a single study program (program studi, prodi) as JSON. For its lecturer roster use get_study_program_lecturers. Study-program aspect. "detail" — Get study program detail; "desc" — Get study program description; "num-students-lecturers" — Get student and lecturer counts; "riwayat" — Get study program name history; "biaya-kuliah" — Get study program tuition range.
| Name | Required | Description | Default |
|---|---|---|---|
| aspect | Yes | ||
| id_prodi | Yes | Opaque study program (prodi) id, copied exactly from a search_pddikti result (entity "prodi"). Do not guess or construct it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds that each call returns exactly one aspect as JSON (a per-call granularity constraint), but says nothing about error behavior or what an unknown prodi id yields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose and the sibling redirect are front-loaded in the first two sentences. However, the dangling fragment "Study-program aspect." is redundant filler before the enum list, which is otherwise dense and useful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and five aspects that each return a different shape (detail vs. counts vs. tuition range), the description never indicates what fields or structure to expect. Enough to invoke, but not enough to interpret results confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50% — id_prodi is documented in the schema, but aspect has no schema description. The description compensates by defining all five enum values, though several definitions are terse restatements ("detail — Get study program detail").
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (fetch) and resource (one aspect of a single study program) and explicitly enumerates the five aspect values, so the agent knows exactly what the tool returns. It also names the sibling get_study_program_lecturers for the related-but-different roster data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes lecturer-roster needs to get_study_program_lecturers, which is a clear alternative/exclusion. It doesn't contrast with search_pddikti or get_university_study_programs, but the aspect enum effectively communicates what each call is for.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_study_program_lecturersGet study program lecturersARead-onlyIdempotent
List the lecturers attached to one study program in a semester. Pass a returned lecturer id to get_lecturer for their profile. Returns a JSON list. Which lecturer set. "dosen-homebase" — Get homebase lecturers by semester; "dosen-penghitung-ratio" — Get lecturers used for ratio calculations.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | ||
| id_prodi | Yes | Opaque study program (prodi) id, copied exactly from a search_pddikti result (entity "prodi"). Do not guess or construct it. | |
| semester | Yes | Semester code YYYYS: year plus 1 (odd/ganjil) or 2 (even/genap), e.g. "20241". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, so the safety profile is covered structurally. The description adds only the return shape ('Returns a JSON list') and says nothing about pagination or result volume, which matters for an openWorldHint tool that could return long rosters.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and short, but the sentence fragments 'Which lecturer set.' and 'Returns a JSON list.' read as dangling labels rather than clean prose, and the enum explanations arrive after them. Content is dense enough to justify the length, but the ordering could be tighter.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description does its job by stating the return is a JSON list and by pointing to get_lecturer for follow-up detail. The only real gap is unspecified pagination or result-size behavior for a potentially long lecturer roster.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%: id_prodi and semester are documented in the schema, but the `kind` enum has no schema description — the description supplies the meaning of both enum values, which is genuine added value beyond the schema. It adds nothing extra for id_prodi or semester, but none is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource with explicit scope: 'List the lecturers attached to one study program in a semester.' The scope narrows it cleanly against the university-level sibling get_university_lecturers, and it names get_lecturer as the follow-up.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: pass a returned lecturer id to get_lecturer for the profile, and disambiguates the two lecturer sets enumerated by `kind` ('dosen-homebase' vs 'dosen-penghitung-ratio'). This is exactly the when-to-use guidance the enum alone cannot convey.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_universityGet university infoARead-onlyIdempotent
Fetch one aspect of a single university (perguruan tinggi, PT) as JSON, or its logo as an image. Use for profile, accreditation, and history questions about one university; for its study programs or lecturers use get_university_study_programs or get_university_lecturers. University aspect. "detail" — Get university detail; "rasio" — Get lecturer-student ratio by university; "mahasiswa" — Get university student metrics; "waktu-studi" — Get average study duration by university; "riwayat" — Get university name history; "biaya-kuliah" — Get tuition range by university; "fasilitas" — Get university facilities metadata; "logo" — University logo (image bytes).
| Name | Required | Description | Default |
|---|---|---|---|
| id_pt | Yes | Opaque university (PT) id, copied exactly from a search_pddikti result (entity "pt"). Do not guess or construct it. | |
| aspect | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, open-world, so the safety profile is covered. The description adds value annotations do not: the return type varies by aspect (JSON vs. raw image bytes for 'logo'), which materially affects how an agent handles the response. It does not mention rate limits or failures, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then routing, then the aspect legend, which is the right order. The fragment 'University aspect.' is redundant filler before the list, and the legend is dense but every entry carries distinct meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description must convey return shape, and it does at a coarse level (JSON per aspect, image bytes for logo). Given eight aspects with heterogeneous payloads, it could say a bit more about what each aspect returns, but an agent has enough to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50% and the enum values carry no per-value descriptions in the schema, but the description compensates fully by defining all eight aspect values (detail, rasio, mahasiswa, waktu-studi, riwayat, biaya-kuliah, fasilitas, logo). The id_pt constraint ('copied exactly from a search_pddikti result, do not guess') is in the schema and reinforced by the workflow implied here.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch one aspect of a single university ... as JSON, or its logo as an image') and scopes it to a single institution. It explicitly distinguishes itself from the two sibling tools an agent would most likely confuse it with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit positive usage ('profile, accreditation, and history questions about one university') and explicit exclusions with named alternatives ('for its study programs or lecturers use get_university_study_programs or get_university_lecturers'). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_university_lecturersGet university lecturersARead-onlyIdempotent
List all lecturers of one university for a semester, merged across its active study programs and deduplicated. Slower than other tools because it fans out one request per program; prefer get_study_program_lecturers when one program is enough. Returns a JSON list.
| Name | Required | Description | Default |
|---|---|---|---|
| id_pt | Yes | Opaque university (PT) id, copied exactly from a search_pddikti result (entity "pt"). Do not guess or construct it. | |
| semester | Yes | Semester code YYYYS: year plus 1 (odd/ganjil) or 2 (even/genap), e.g. "20241". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and non-destructive, so safety is covered; the description adds genuinely new behavior: per-program fan-out cost, cross-program merging and deduplication, and the return shape (JSON list). It stops short of noting pagination, error behavior for an unknown id_pt, or what happens when a program has no lecturers, which keeps it off a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, zero waste, and the most decision-relevant facts are front-loaded: what it returns, then the cost, then the alternative. No restatement of the title or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read tool with full schema coverage, rich annotations and no output schema, nothing an agent needs is missing; the fan-out warning and the sibling routing rule cover the only real failure modes (slow call, wrong tool).
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% — both id_pt (with an explicit "do not guess or construct" warning) and the YYYYS semester pattern are fully documented in the schema. The description reinforces the scope of both parameters but adds no syntax or edge-case detail beyond it, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("list all lecturers") plus scope (one university, one semester) and the aggregation semantics (merged across active study programs, deduplicated). It explicitly distinguishes itself from the sibling get_study_program_lecturers, so an agent can route between the two without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit alternative and the condition that selects it: "prefer get_study_program_lecturers when one program is enough." The implied inverse — use this tool when the whole university is needed — is unambiguous, and the cost warning ("slower ... fans out one request per program") reinforces the trade-off.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_university_study_programsGet university study programsARead-onlyIdempotent
List every study program (prodi) a university offered in one semester, with each program’s id. Pass a returned prodi id to get_study_program for details. Returns a JSON list.
| Name | Required | Description | Default |
|---|---|---|---|
| id_pt | Yes | Opaque university (PT) id, copied exactly from a search_pddikti result (entity "pt"). Do not guess or construct it. | |
| semester | Yes | Semester code YYYYS: year plus 1 (odd/ganjil) or 2 (even/genap), e.g. "20241". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and open-world behavior, so the safety profile is covered. The description adds value beyond that by disclosing the return shape ('Returns a JSON list') and the intended downstream chaining, though it says nothing about result volume, pagination, or what happens when a university has no programs in that semester.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with what is returned, followed by the follow-up call and the return type. Nothing is redundant and every sentence carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With only two well-documented parameters and no output schema, the description adequately covers what the tool yields ('a JSON list' of programs with ids) and how to continue. It would be stronger if it hinted at the per-program fields beyond id or at empty-result behavior, but nothing critical for a correct call is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: id_pt is documented as an opaque id copied from search_pddikti results and semester as a YYYYS pattern code. The description only reinforces the semester framing ('in one semester') and adds no format or constraint detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List every study program (prodi) a university offered in one semester') and adds a scope qualifier (per university, per semester). It also distinguishes itself from the detail sibling by naming get_study_program as the follow-up for a single program's details.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear usage context — this is the listing step before calling get_study_program with a returned prodi id — which is explicit chaining guidance an agent can act on. It stops short of distinguishing itself from sibling listing tools such as list_study_programs_by_field or get_university, so no explicit exclusion is offered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_study_programs_by_fieldList study programs by fieldARead-onlyIdempotent
List study programs nationwide for one field of study (bidang ilmu), for discovering which programs exist in a discipline. Needs no id. Returns a JSON list. Field of study (bidang ilmu). "agama" — religion; "ekonomi" — economics; "humaniora" — humanities; "kesehatan" — health; "mipa" — science; "pendidikan" — education; "pertanian" — agriculture; "seni" — arts; "sosial" — social; "teknik" — engineering.
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so safety is covered. The description adds a modest amount beyond that ('Returns a JSON list', 'Needs no id') but says nothing about result volume, completeness of the nationwide set, or ordering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and scope are front-loaded in the first sentence, and the enum glossary is dense but earns its space. Minor waste: 'field of study (bidang ilmu)' is restated as its own sentence, duplicating the opening parenthetical.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A single-required-enum, read-only tool with a stated return type ('a JSON list') and every enum value translated. With no output schema, the description covers what an agent needs; only result shape detail would improve it marginally.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the parameter meaning and it does: it glosses the Indonesian enum terms in English (agama=religion, teknik=engineering, etc.), which the raw enum does not. It only falls short of 5 because the same single parameter is described twice.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List study programs') plus a scope qualifier ('nationwide for one field of study') that implicitly separates it from the university-scoped sibling get_university_study_programs. It does not name a sibling outright, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'for discovering which programs exist in a discipline' and 'Needs no id' give a clear use context and implicitly contrast with the id-based lookup tools. No explicit when-not or named alternative, so it is clear context without exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_pddiktiSearch PDDiktiARead-onlyIdempotent
Search Indonesian higher-education records by keyword and return matching entries with their opaque ids. Start here: every other tool needs an id from this search. Returns a JSON list (empty when nothing matches). Entity to search. "all" — all entities; "pt" — universities; "prodi" — study programs; "dosen" — lecturers; "mhs" — students.
| Name | Required | Description | Default |
|---|---|---|---|
| entity | Yes | ||
| keyword | Yes | Name, NIM (student number), NIDN (lecturer number), or code to search for. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this as read-only, idempotent, open-world, and non-destructive. The description adds useful behavioral context by saying the result is a JSON list and is empty when nothing matches, though it does not discuss pagination or result limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is short and front-loads the main instruction, the entry-point role, and the return shape. The trailing enum explanation is useful but slightly awkwardly appended.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter search tool with no output schema, the description provides the key context: what it returns, when to use it, and what the entity options mean. An agent has enough information to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%, but the description compensates by explaining the 'entity' enum values: all, pt, prodi, dosen, and mhs. The keyword parameter is documented in the schema, so the combined description and schema give adequate parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: search Indonesian higher-education records by keyword and return matching entries with opaque ids. It also distinguishes this as the entry-point tool, noting that every other tool needs an id from this search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly explains when to use this tool first and why: every sibling get_* tool depends on ids returned here. It does not explicitly describe when not to use it, but the usage context is clear enough for an agent.
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.
10 tool updates
v1.2.0- First observed
get_lecturer - First observed
get_statistics - First observed
get_student - First observed
get_study_program - First observed
get_study_program_lecturers - First observed
get_university - First observed
get_university_lecturers - First observed
get_university_study_programs - First observed
list_study_programs_by_field - First observed
search_pddikti
TDQS
Scored across 10 tools
Tools mostly map to distinct resources and actions: search_pddikti finds IDs, get_* tools retrieve one entity or aspect, and list_study_programs_by_field supports nationwide discovery. The closest overlap is get_university_lecturers versus get_study_program_lecturers, but their scope and performance trade-off are clearly described, so confusion is limited.
All names use snake_case with a recognizable verb_noun structure. Minor inconsistency appears where list-returning tools use get_ (get_university_lecturers, get_study_program_lecturers, get_university_study_programs) while another uses list_, and search_pddikti uses the server name rather than an entity noun.
Ten tools is well within the appropriate 3–15 range for this domain. Each tool covers a distinct entity or operation, and the multi-aspect get_* tools consolidate related endpoints rather than inflating the surface.
The read-only surface covers search across all entity types, university/program/lecturer/student detail, national statistics, and field-based program discovery, with no obvious dead ends. Minor gaps remain: there is no general list-all-universities or list-all-lecturers operation, and student access is intentionally single-record only.
Maintenance
Related MCP Connectors
Indonesian public data: regions, postal codes, holidays, prayer times, BMKG, schools, wages
Semantic retrieval across 295,000+ Indonesian regulations and 5.6M+ legal provisions, updated daily. Give AI direct access to Indonesian regulatory knowledge before reasoning.
Indonesia reference data: holidays, wages, regions, postal codes, tax, food prices, KBLI, BPS stats
Normalized official data with provenance, aggregations, insights, free samples and agent access.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides access to Turkish higher education data, enabling search and retrieval of detailed information about bachelor's and associate degree programs from universities across Turkey.MIT
- AlicenseBqualityCmaintenanceEnables AI clients to access official Indonesian statistical data from Badan Pusat Statistik (BPS) through natural language queries. It provides over 20 tools for retrieving demographic, economic, and trade data with support for bilingual responses and fuzzy domain matching.34233 npm6MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to search and retrieve data from Indonesia's higher education database (PDDIKTI), including universities, lecturers, study programs, and students.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to search and access data from Indonesia's Science and Technology Index (SINTA), including lecturer profiles, journal accreditation, and scientific publications.MIT