@akredit/acik-mcp 0.1.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +94 -8
  2. package/dist/apps/acik-mcp/src/araclar.d.ts +2 -28
  3. package/dist/apps/acik-mcp/src/araclar.js +29 -114
  4. package/dist/apps/acik-mcp/src/bologna.d.ts +291 -0
  5. package/dist/apps/acik-mcp/src/bologna.js +1693 -0
  6. package/dist/apps/acik-mcp/src/genel-ayristir.d.ts +92 -0
  7. package/dist/apps/acik-mcp/src/genel-ayristir.js +489 -0
  8. package/dist/apps/acik-mcp/src/http.d.ts +31 -3
  9. package/dist/apps/acik-mcp/src/http.js +90 -17
  10. package/dist/apps/acik-mcp/src/index.d.ts +7 -0
  11. package/dist/apps/acik-mcp/src/index.js +7 -0
  12. package/dist/apps/acik-mcp/src/kaynak-dogrula.d.ts +94 -0
  13. package/dist/apps/acik-mcp/src/kaynak-dogrula.js +460 -0
  14. package/dist/apps/acik-mcp/src/kaynak.d.ts +72 -0
  15. package/dist/apps/acik-mcp/src/kaynak.js +155 -0
  16. package/dist/apps/acik-mcp/src/kisisel-veri.d.ts +16 -2
  17. package/dist/apps/acik-mcp/src/kisisel-veri.js +78 -15
  18. package/dist/apps/acik-mcp/src/not-tablosu.d.ts +39 -0
  19. package/dist/apps/acik-mcp/src/not-tablosu.js +268 -0
  20. package/dist/apps/acik-mcp/src/promptlar.d.ts +48 -0
  21. package/dist/apps/acik-mcp/src/promptlar.js +323 -0
  22. package/dist/apps/acik-mcp/src/sunucu.d.ts +12 -4
  23. package/dist/apps/acik-mcp/src/sunucu.js +108 -24
  24. package/dist/apps/acik-mcp/src/temel.d.ts +59 -0
  25. package/dist/apps/acik-mcp/src/temel.js +10 -0
  26. package/dist/apps/acik-mcp/src/yardim.d.ts +56 -0
  27. package/dist/apps/acik-mcp/src/yardim.js +381 -0
  28. package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.d.ts +29 -0
  29. package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.js +200 -0
  30. package/dist/packages/cekirdek/src/bologna/taslak.d.ts +19 -3
  31. package/dist/packages/cekirdek/src/bologna/taslak.js +70 -9
  32. package/dist/packages/cekirdek/src/turler.d.ts +8 -0
  33. package/package.json +5 -3
@@ -0,0 +1,323 @@
1
+ // MCP istemleri (prompts). İstemciler bunları eğik çizgi komutu ya da hazır başlangıç olarak gösterir.
2
+ // İlke (İP-49): sıfır kullanıcı müdahalesiyle süreci hızlıca ama denetimli, teyitli tamamlamak. Varsayılan yol
3
+ // "4) Hepsini akıllıca doldur"dur: asistan her alan için en iyi seçeneği kendisi seçer, sunucu denetiminden
4
+ // geçmeyeni düzeltip yeniden denetler, sonunda tek bir özet gösterir. Hoca isterse numarayla düzeltir.
5
+ // Halüsinasyona yer yok: kitap, kişi, tarih, sayı uydurulmaz; kaynaklar kaynak_dogrula'dan geçmeden önerilmez.
6
+ //
7
+ // Dış inceleme 2026-11-05: akıllı doldurma kalır, ama bilinmesi mümkün olmayan ve resmî taahhüt olan bilgiler
8
+ // (değerlendirme yöntemi ve yüzdeleri, uygulama/lab, kullanılan kaynaklar, sayfada yoksa amaç ve içerik) sayfadan
9
+ // okunur; yoksa asistan bunları TEK mesajda, numaralı ve varsayılan seçenekli sorar, uydurmaz. Ders adından
10
+ // üretilen her şey "öneri" etiketiyle gösterilir. OBS'ye yazmadan önce alan alan önceki/yeni değer özeti gösterilir,
11
+ // hoca bir kez "kaydet" der; kayıttan sonra sayfa geri okunur ve obs_karsilastir ile doğrulanır.
12
+ //
13
+ // İstem metni veri değil asistana yönergedir; içinde hocanın girdisi yalnız isteğe bağlı kısa bağımsız
14
+ // değişkenler olarak yer alır (ders kodu, çerçeve) ve sınırlanır. Giriş bilgisi (parola, doğrulama kodu,
15
+ // çerez) istenmez; görülürse reddedilir ve kaydedilmez (ilke 8).
16
+ import { z } from "zod";
17
+ import { arac, KOD_KAYNAGI } from "./temel.js";
18
+ import { GIRIS_KURALI_HOCAYA } from "./yardim.js";
19
+ /** Giriş bilgisi kuralı: bütün istemlerde aynen yer alır. */
20
+ export const GIRIS_BILGISI_KURALI = [
21
+ "Giriş bilgisi kuralı (değişmez):",
22
+ "- Hocadan kullanıcı adı, parola, doğrulama kodu, SMS kodu ya da çerez asla isteme. Bunları sohbete yazmasını önerme.",
23
+ "- Hoca bunları kendisi yalnız OBS'nin kendi giriş ekranına yazar. Bunu hocaya ilk adımda açıkça söyle.",
24
+ "- Hoca yine de sohbete parola yazarsa kullanma, hiçbir araca gönderme; silmesini ve OBS'de parolasını değiştirmesini öner.",
25
+ "- CAPTCHA'yı sen çözmezsin, hoca çözer.",
26
+ '- OBS\'de Kaydet düğmesine yalnız hocanın açık izniyle basarsın: önce alan alan önceki ve yeni değer özetini gösterirsin, hoca bir kez "kaydet" der. İzin yoksa basmazsın. Onaya ya da dış kuruma Gönder düğmesine hiç basmazsın; onu hoca basar.',
27
+ ].join("\n");
28
+ /** Akıllı doldurma kısayolu: bütün menülerde aynı numara ve aynı anlam. */
29
+ export const AKILLI_DOLDURMA_SECENEGI = "4) Hepsini akıllıca doldur (önerilen)";
30
+ /** "4) Hepsini akıllıca doldur" seçildiğinde (ya da hoca "sen seç", "hepsi" derse) izlenecek yol. */
31
+ /** Bilinmesi mümkün olmayan ve resmî taahhüt olan bilgiler: sayfadan okunur, yoksa tek mesajda sorulur. */
32
+ export const TEYIT_SORULARI = [
33
+ "Teyit gerektiren bilgiler (asla tahmin etme):",
34
+ "- Değerlendirme yöntemleri ve yüzdeleri (vize, final, ödev, proje...).",
35
+ "- Dersin uygulama ya da laboratuvar içerip içermediği.",
36
+ "- Derste gerçekten kullanılan kaynaklar ve ders notları.",
37
+ "- Sayfada yoksa dersin amacı ve içeriği.",
38
+ "Bunları önce sayfadan oku. Sayfada olmayanları hocaya TEK mesajda, birlikte, numaralı ve her birine varsayılan seçenekle sor. Örnek:",
39
+ ' "Birkaç bilgiyi yalnız siz bilebilirsiniz. Numarayla yanıtlayın; boş bırakırsanız parantezdeki varsayılanı kullanırım.',
40
+ " 1) Değerlendirme: a) Vize %40, Final %60 (varsayılan) b) Vize %30, Ödev %20, Final %50 c) başka: yazın",
41
+ " 2) Uygulama ya da laboratuvar var mı? a) yok (varsayılan, T+U+L'ye göre) b) var",
42
+ ' 3) Kullandığınız kitap ya da notlar: a) önereyim, siz seçin (varsayılan) b) şunları kullanıyorum: yazın"',
43
+ "Hocanın seçtiği varsayılan da hoca teyidi sayılır (degerlendirmeDayanagi: 'hoca'). Yanıt gelmeden değerlendirme yüzdesi yazma paketine girmez.",
44
+ ].join("\n");
45
+ export const AKILLI_DOLDURMA = [
46
+ "Hepsini akıllıca doldur (seçenek 4):",
47
+ "- Önce teyit gerektiren bilgileri sayfadan oku; sayfada olmayanları hocaya TEK mesajda birlikte sor (aşağıdaki kural). Bundan sonra ara soru sorma.",
48
+ "- Kalan her alan için seçeneklerini kendin yaz ve en iyisini kendin seç. Ders adından ve program çıktılarından ürettiğin her şeyi 'öneri' etiketiyle göster.",
49
+ "- Her seçimi ilgili araçla denetle (bologna_taslak_denetle, bologna_durum, rapor_dil_denetle). Geçmeyeni düzelt ve yeniden denetle; geçene kadar hocaya gösterme. Üç denemede geçmezse o alanı EKSİK bırak.",
50
+ "- Sayıları (toplam, yüzde, iş yükü, sağlanma düzeyi) yalnız araç yanıtından al.",
51
+ "- Uydurma yok: hocanın vermediği ve doğrulayamadığın bilgi (kişi, kitap, tarih, sayı, toplantı) EKSİK kalır. Kaynaklar yalnız kaynak_dogrula'dan 'dogrulandi' dönenlerdir.",
52
+ "- Araç sonucunu 'geçer' diye sunma: 'biçim denetiminden geçti; içerik teyidi: hoca' de.",
53
+ "- Bitince tek bir özet göster: numara, alan, ne yazdığın (ilk cümle ya da kısa özet), kaynağı (sayfa, hoca ya da öneri), biçim denetimi sonucu. Altına şunu yaz:",
54
+ ' "Şu alanları şöyle doldurdum; öneri yazanlar benim önerimdir, içeriği siz teyit edersiniz. Değiştirmek istediğiniz alanın numarasını yazın; yoksa devam diyebilirsiniz."',
55
+ "",
56
+ TEYIT_SORULARI,
57
+ ].join("\n");
58
+ /** Bütün istemlerde ortak çalışma biçimi. */
59
+ export const ORTAK_KURALLAR = [
60
+ "Çalışma biçimi:",
61
+ "- Amaç: hocanın en az müdahalesiyle işi hızlı ama denetimli ve teyitli bitirmek.",
62
+ "- Karşındaki hoca yapay zekâyı belki ilk kez kullanıyor. Kısa, sade Türkçe yaz. Araç adı, JSON ve teknik terim gösterme.",
63
+ "- Her mesajda tek iş iste. Ne yapacağını ve ekranda ne göreceğini söyle.",
64
+ `- Her seçim menüsünde 1-3 arası kısa seçenek ve her zaman son satırda aynı kısayol olsun: "${AKILLI_DOLDURMA_SECENEGI}". 4 her menüde aynı anlama gelir.`,
65
+ '- Hoca tek rakamla seçer. "sen seç", "hepsi", "hepsi 1" gibi yanıtlar toplu onaydır; kalan bütün adımlara uygula, her adımı yeniden sorma.',
66
+ "- Sunucu metin üretmez. Seçenek metinlerini sen yazarsın; sunucu kural verir, denetler ve sayıları hesaplar.",
67
+ "- Sayıları (toplam, yüzde, iş yükü, sağlanma düzeyi) yalnız araç yanıtından al, kendin toplama.",
68
+ "- Uydurma yok: kişi, kitap, ISBN, DOI, tarih, toplantı, sayı uydurma. Bilinmeyen bilgi EKSİK kalır.",
69
+ "- Kaynak: önce sayfadaki ve hocanın kullandığı kaynakları al. Öneri gerekiyorsa kendi web arama aracınla aday bul, her birini kaynak_dogrula ile sorgula ve yalnız 'katalogda_bulundu' dönenleri künyesi ve bağlantısıyla 'öneri' olarak göster; uygunluğuna hoca karar verir. Katalogda bulunmayan kaynağı kendin önerme; hoca kullandığını söylerse 'hoca_beyani' olarak yaz.",
70
+ "- Bologna sayfasındaki Dersi Verenler, Yardımcılar ve Koordinatör satırları hocanın yayımlanmış görev bilgisidir: olduğu gibi oku, koru, değiştirme.",
71
+ "- Öğrenci adı ya da numarası sohbete yapıştırılmaz: hoca Excel'de Ad, Soyad, Numara sütunlarını silip yalnız not sütunlarını yapıştırır ya da https://akredit.org/deneyin hesaplayıcısını kullanır. Not tablosu aracı kimlik sütunu görürse tabloyu işlemez.",
72
+ "- Engel çıkarsa (tarayıcı aracı yok, site açılmıyor, giriş ekranı, CAPTCHA, çerçeve, oturum düştü) engel_coz ile adımları al ve hocaya tek tek göster.",
73
+ "- Her araç yanıtındaki sonrakiAdim önerisine uy.",
74
+ "- Sonunda kopyalanabilir bir çıktı ver. Onaya ya da dış kuruma gönderimi hoca yapar.",
75
+ "",
76
+ AKILLI_DOLDURMA,
77
+ "",
78
+ GIRIS_BILGISI_KURALI,
79
+ ].join("\n");
80
+ const kisa = (v, n = 40) => v
81
+ ? v
82
+ .replace(/[\r\n`]/gu, " ")
83
+ .trim()
84
+ .slice(0, n)
85
+ : "";
86
+ const ODAKLAR = [
87
+ ["1", "Bologna ders paketini denetle ve boş alanları doldur", "bologna_doldur"],
88
+ ["2", "Ders öğrenme çıktısı yaz ya da iyileştir", "cikti_yaz"],
89
+ ["3", "ÇK ve PÇ sağlanma düzeyini hesapla", "ck_pc_hesabi"],
90
+ ["4", "YÖKAK, MÜDEK, FEDEK, EPDAD ya da SABAK ölçüt ve kanıt rehberi", "olcut_rehberi"],
91
+ ["5", "Öz değerlendirme metninin dil denetimi", "dil_denetimi"],
92
+ ];
93
+ /** akredit başlangıcının sorduğu odak menüsü; site örnek konuşmayı buradan okur. */
94
+ export const AKREDIT_ODAKLARI = ODAKLAR.map(([no, ad]) => ({ no, ad }));
95
+ /** OBS'ye tarayıcıyla giriş: Bologna akışının ilk adımı. */
96
+ const obsGirisi = () => [
97
+ "Adım 0. OBS'ye giriş (tarayıcı).",
98
+ ` Hocaya önce şunu aynen söyle: "${GIRIS_KURALI_HOCAYA}"`,
99
+ " Tarayıcı aracın varsa (Claude in Chrome, Claude masaüstü tarayıcı paneli, ChatGPT ajan modu gibi):",
100
+ " a) Hocaya OBS ya da Bologna sisteminin adresini sor (kurumun sitesindeki bağlantı). Adresi kenardaki tarayıcıda aç.",
101
+ ' b) Giriş ekranı gelince dur ve şunu yaz: "Kullanıcı adınızı ve parolanızı şimdi açılan OBS ekranına kendiniz yazın. Bitince bana yalnız girdim yazın."',
102
+ " c) CAPTCHA ya da doğrulama kodu gelirse hocanın kendisinin girmesini bekle; sen girme.",
103
+ " d) Hoca girdim deyince ders listesini oku ve numaralı göster. Sor: hangi ders? Hoca numara yazar.",
104
+ " e) Dersin Bologna (ders bilgi paketi) sayfasını oku. Okuduğun metni ya da HTML'i bologna_ayristir'e ver.",
105
+ " Tarayıcı aracın yoksa: hocaya sor: 1) kurulumu birlikte yapalım (önerilen) 2) sayfayı kopyalayıp yapıştırayım 3) sıfırdan başlayalım.",
106
+ " 1 seçilirse kurulum_yardimi'nı hocanın asistanıyla (istemci) çağır, adımları tek tek göster.",
107
+ ' 2 seçilirse şunu yaz: "OBS\'de dersinizin Bologna sayfasını açın. Sayfada Ctrl+A ile hepsini seçin, Ctrl+C ile kopyalayın, buraya Ctrl+V ile yapıştırın."',
108
+ " Herhangi bir engelde engel_coz'u ilgili engel türüyle çağır.",
109
+ ].join("\n");
110
+ /** Kaynak: önce sayfadaki ve hocanın kullandığı; öneri yalnız katalogda bulunan, "öneri" etiketiyle. */
111
+ const KAYNAK_ADIMI = [
112
+ " Kaynaklar: önce sayfadaki kaynakları al; derste kullanılan kaynakları teyit sorularıyla birlikte (tek mesajda) sor.",
113
+ " Öneri gerekiyorsa kendi web arama aracınla 4-6 aday eser bul; her adayı DOI, ISBN ya da başlık, yazar, tür ve biliniyorsa yılla kaynak_dogrula'ya ver (çağrı başına en çok 5).",
114
+ " Yalnız 'katalogda_bulundu' dönenleri 'öneri' etiketiyle, künyesi ve bağlantısıyla göster; uygunluğuna hoca karar verir. Bologna araçlarına kaynağı { kunye, dogrulama: { durum, baglanti, imza } } olarak aynen ver.",
115
+ " Katalogda bulunmayan eseri kendin önerme. Hoca bir eseri kullandığını söylerse (çoğu Türkçe kitap katalogda yoktur) künyeyi yalnız hocanın verdiği bilgiyle yaz ve dogrulama: 'hoca_beyani' ver; eksik alanı uydurma, EKSİK bırak.",
116
+ ].join("\n");
117
+ /** OBS'ye yazma: önce önceki/yeni özeti, tek "kaydet", sonra geri okuma ve obs_karsilastir. */
118
+ const OBS_YAZMA = [
119
+ "6. OBS'ye yazma.",
120
+ " Önce bologna_kopya_metni'ni çağır (OBS'deki şimdiki değerleri oncekiDegerler olarak ver). Yazma paketine alınmayanları (doğrulanmamış kaynak, teyitsiz değerlendirme yüzdesi) hocaya söyle; onları yazma.",
121
+ " Hocaya degisiklikOzeti'ni alan alan göster: numara, sekme, alan, önceki değer, yeni değer (uzunsa ilk cümle).",
122
+ " Tarayıcı aracın yoksa doğrudan 3'ü uygula. Varsa sor:",
123
+ " 1) Formu ben doldurayım, Kaydet'e siz basın",
124
+ ' 2) Formu ben doldurup kaydedeyim: yukarıdaki özeti onaylıyorsanız bir kez "kaydet" yazın',
125
+ " 3) Alan alan kopyalayın",
126
+ " 1: OBS düzenleme ekranında sekme sekme ilgili kutulara yaz. Her sekmeden sonra dur ve hocadan gözle denetleyip Kaydet'e basmasını iste.",
127
+ ' 2: Yalnız hoca "kaydet" yazdıysa, yalnız özetteki alanları yaz ve kaydet. Her sekmeden önce ders kodunu ve dönemi sayfada denetle; beklenenden farklıysa yazmayı durdur ve hocaya söyle.',
128
+ " 3: bologna_kopya_metni metnini her alan ayrı kod bloğunda ver, hangi sekmedeki hangi kutuya yapıştırılacağını yaz.",
129
+ " Kayıttan sonra (1 ve 2): sayfayı geri oku, obs_karsilastir'e obsKarsilastirGirdisi'ni ve okuduğun metni ver. dur true ise yazmayı bırak, nedenini söyle. Farklı alanları hocaya göster.",
130
+ " Her durumda: Dersin Yetkilileri satırlarına dokunma, OBS'deki hâliyle kalsın. Parolayı asla isteme; o yalnız hocadadır. Onaya gönderme (Gönder) düğmesine sen basma.",
131
+ ].join("\n");
132
+ const bolognaAkisi = (dersKodu) => [
133
+ `Odak: Bologna ders bilgi paketini denetle, eksik ve zayıf alanları tamamla.${dersKodu ? ` Ders: ${dersKodu}.` : ""}`,
134
+ "",
135
+ "Akış:",
136
+ obsGirisi(),
137
+ "1. Ayrıştır. bologna_ayristir önce Proliz biçimini, olmazsa genel ayrıştırıcıyı dener (sekmeli düz metin de olur). Bulunamayan alanları ve ingilizce değerini not al; sonraki araçlara ingilizce değerini aynen ver.",
138
+ " Dersi Verenler, Yardımcılar ve Koordinatör satırları yetkililer alanında olduğu gibi gelir; onları değiştirme, sonraki çağrılarda aynen geri ver.",
139
+ " Program çıktıları sayfası da varsa onu da ayrıştır ve P kodlarını sakla; P kodlarını asla ezberden yazma.",
140
+ "2. Denetle. bologna_denetle'yi çağır. Sonucu tek tabloda göster: numara, alan, durum (boş, zayıf, tamam), kısa neden. Kalan alan sayısını söyle.",
141
+ ` Sor: 1) alan alan seçenek göstererek dolduralım 2) yalnız boş olanlar 3) numaraları kendim seçeyim ${AKILLI_DOLDURMA_SECENEGI}.`,
142
+ "3. Teyit. Değerlendirme yöntemi ve yüzdeleri, uygulama/lab, kullanılan kaynaklar ve (sayfada yoksa) amaç ve içerik yalnız hocanın bilebileceği bilgilerdir. Sayfada olmayanları hocaya TEK mesajda, numaralı ve varsayılan seçenekli sor (akıllı doldurmada da). Uydurma.",
143
+ " Amaç ve içerik için hocanın verdiği kısa odak yoksa ders adından ve program çıktılarından 'öneri' yaz ve öneri olduğunu söyle.",
144
+ "4. Her alan için: bologna_alan_rehberi'ni çağır. Kurala uyan 2-3 kısa seçenek yaz.",
145
+ " Seçenekleri bologna_taslak_denetle ile denetle; biçim denetiminden geçmeyeni düzelt ve yeniden denetle. Uzun metinlerde ilk cümleyi ve kelime sayısını göster. Sonucu 'biçim denetiminden geçti; içerik teyidi: hoca' diye sun.",
146
+ " Akıllı doldurmada (4) en iyi seçeneği kendin seç ve hocaya alan alan sorma; sonunda tek özet göster.",
147
+ " Haftalık konular: 14 hafta, ara sınav ve yarıyıl sonu haftası görünür. Değerlendirme: yalnız sayfadan ya da hocadan; degerlendirmeDayanagi'ni ('sayfa' ya da 'hoca') ver.",
148
+ " AKTS iş yükü: is_yuku_oner'i sayfadaki değerlendirme kalemleriyle çağır; derste olmayan etkinliğe satır açılmaz. Sayfadan okunan iş yüküyle öneriyi ayrı göster. AKTS'yi değiştirme.",
149
+ " Öğrenme çıktısı ile program çıktısı katkı tablosu: yalnız ayrıştırılan P kodlarıyla öner.",
150
+ KAYNAK_ADIMI,
151
+ '5. Her alandan sonra bologna_durum\'u çağır. Alan alan ilerliyorsan kalan alan sayısını tek cümleyle söyle: "Kalan 3 alan."',
152
+ OBS_YAZMA,
153
+ '7. Doğrulama. Kayıttan sonra sayfayı yeniden oku (tarayıcı varsa) ya da yeniden yapıştırmasını iste; obs_karsilastir ve bologna_denetle ile denetle. Fark yoksa "Kaydedildi ve geri okunarak doğrulandı; içerik teyidi sizde" de.',
154
+ " Sor: 1) başka ders 2) bir alanı değiştir 3) bitir.",
155
+ ].join("\n");
156
+ const ciktiAkisi = (dersKodu) => [
157
+ `Odak: ders öğrenme çıktısı yaz ya da iyileştir.${dersKodu ? ` Ders: ${dersKodu}.` : ""}`,
158
+ "",
159
+ "Akış:",
160
+ "1. Sor: 1) OBS'deki mevcut çıktılarımı okuyalım (tarayıcı aracın varsa OBS girişi için Bologna akışındaki Adım 0'ı izle) 2) çıktılarımı yapıştırayım 3) sıfırdan yaz: ders adı, AKTS, T+U+L ve kısa içerik vereyim.",
161
+ "2. bologna_alan_rehberi'ni alan=ciktilar ile çağır: kurallar, Bloom fiilleri, 4-10 sınırı (5-8 önerilir).",
162
+ `3. Sor: 1) her çıktı için seçenek göstereyim 2) yalnız zayıf olanları iyileştireyim 3) numaraları kendim seçeyim ${AKILLI_DOLDURMA_SECENEGI}.`,
163
+ " 1-3: her çıktı için 2-3 seçenek yaz (farklı Bloom düzeyi ya da ölçme biçimi), türünü (Bilgi, Beceri, Yetkinlik) yaz.",
164
+ " 4: her çıktı için en iyi ifadeyi kendin seç; hocaya ara soru sorma.",
165
+ "4. Bütün listeyi bologna_taslak_denetle ile denetle (ciktilar alanı; İngilizce isteniyorsa en alanıyla). Geçmeyeni düzelt ve yeniden denetle.",
166
+ "5. Tek özet göster: numara, çıktı, tür, denetim sonucu. Değiştirmek istediği numarayı sor.",
167
+ " Program çıktısı katkısı: P kodları ayrıştırıldıysa onlarla doldur; yoksa P kodu uydurma, hocadan program çıktıları sayfasını iste.",
168
+ "6. bologna_kopya_metni ile Öğrenme Çıktıları sekmesine kopyalanacak metni ver ya da Bologna akışındaki OBS'ye yazma adımını izle.",
169
+ ].join("\n");
170
+ const ckPcAkisi = (dersKodu) => [
171
+ `Odak: ÇK ve PÇ sağlanma düzeyi hesabı.${dersKodu ? ` Ders: ${dersKodu}.` : ""}`,
172
+ "",
173
+ "Akış:",
174
+ '1. Hocaya önce şunu söyle: "Kişisel veriyi yapay zekâ sohbetine yapıştırmayın: Excel\'de Ad, Soyad, Numara sütunlarını silip yalnız not sütunlarını yapıştırın ya da verinin tarayıcınızdan çıkmadığı hesaplayıcıyı kullanın: https://akredit.org/deneyin"',
175
+ " Sonra yalnız not sütunlarını yapıştırmasını iste. İlk satır başlık, her soru bir sütun; başlıkta sınav, soru, ÇK ve tam puan olursa iş biter (ör. Vize S1 (ÇK1) /20). Öğrenciler o1, o2 takma adı alır.",
176
+ "2. Tabloyu not_tablosu_ayristir'e ver.",
177
+ `3. Sor: 1) eşlemeyi tek tek gözden geçirelim 2) yalnız eksikleri soralım 3) eşlemeyi kendim yazayım ${AKILLI_DOLDURMA_SECENEGI}.`,
178
+ " 4: başlıktan okunan eşlemeyi ve varsayılanları kullan: eşik 60, sınav ağırlıkları tablodan. Başlıkta olmayan soru-çıktı eşlemesini ve tam puanı tahmin etme; bunları tek mesajda topluca sor.",
179
+ "4. Hazır olunca ck_pc_hesapla'yı çağır. Sonucu tablo olarak göster; sayıları yalnız yanıttan al.",
180
+ "5. Tek özet: hangi eşlemeyi ve eşiği kullandığını, eşiğin altında kalan çıktıları yaz. Değiştirmek istediği numarayı sor.",
181
+ "6. Sor: 1) eşiğin altındaki çıktılar için iyileştirme notu taslağı 2) farklı eşikle yeniden hesapla 3) bitir. Not taslağında sayı uydurma; sonucu kopyalanabilir tablo olarak ver.",
182
+ ].join("\n");
183
+ const olcutAkisi = (cerceve) => [
184
+ `Odak: ölçüt ve kanıt rehberi.${cerceve ? ` Çerçeve: ${cerceve}.` : ""}`,
185
+ "",
186
+ "Akış:",
187
+ `1. ${cerceve ? "Çerçeve verildi, sorma." : "Sor: hangi çerçeve? 1) YÖKAK kurumsal 2) program akreditasyonu: MÜDEK, FEDEK, EPDAD ya da SABAK. Hoca söylemezse YÖKAK kurumsal ile başla."}`,
188
+ `2. Sor: 1) bir ölçütün resmî metni 2) bir konuda hangi ölçütler var 3) öz değerlendirme iskeleti ${AKILLI_DOLDURMA_SECENEGI.replace("doldur", "hazırla")}: konuya uyan ölçütleri bulup resmî metinleri ve kanıt kontrol listesini tek seferde çıkarırım.`,
189
+ "3. olcut_ara ile ara, en uygun sonuçları al. olcut_getir ile resmî metni kaynağıyla göster. Akıllı hazırlamada en uygun 2-3 ölçütü kendin seç.",
190
+ "4. YÖKAK alt ölçütüyse olgunluk_duzeyleri ile düzeyleri ve örnek kanıtları al. Kanıtın kurumda olup olmadığını tahmin etme: kontrol listesinde 'hocaya sorulacak' yaz; hoca isterse tek mesajda 'var olanların numaraları' diye sor.",
191
+ "5. Programın disiplinini ya da programa özgü bölümünü sen tahmin etme; bilmiyorsan tek soruyla sor.",
192
+ "6. Ölçüt metnini yalnız araç yanıtından alıntıla. DOĞRULANMADI işaretli metni tahminle doldurma.",
193
+ "7. Sonunda tek özet ve kopyalanabilir kanıt kontrol listesi ver: ölçüt, kanıt, durum (var, yok, hocaya sorulacak, EKSİK), not.",
194
+ ].join("\n");
195
+ const dilAkisi = () => [
196
+ "Odak: öz değerlendirme ya da faaliyet raporu metninin dil denetimi.",
197
+ "",
198
+ "Akış:",
199
+ "1. Hocadan metni yapıştırmasını iste. Öğrenci adı ya da numarası varsa göndermeden önce o1, o2 gibi takma adla değiştir.",
200
+ "2. Rapor dönemini metinden çıkar; çıkaramazsan tek soruyla sor.",
201
+ "3. rapor_dil_denetle'yi çağır.",
202
+ `4. Sor: 1) bulguları tek tek göstereyim 2) yalnız önemli olanları düzelteyim 3) numaraları kendim seçeyim ${AKILLI_DOLDURMA_SECENEGI.replace("doldur", "düzelt")}.`,
203
+ " 4: her bulgu için en iyi düzeltmeyi kendin seç. Kanıt ya da sayı gerektiren düzeltmede bilgi uydurma; o yere EKSİK yaz.",
204
+ "5. Düzeltilmiş metni yeniden rapor_dil_denetle ile denetle; kalan bulguları düzelt.",
205
+ "6. Tek özet göster: numara, eski ifade, yeni ifade. Değiştirmek istediği numarayı sor. Sonunda düzeltilmiş metnin tamamını kopyalanabilir biçimde ver.",
206
+ ].join("\n");
207
+ const AKISLAR = {
208
+ bologna_doldur: (a) => bolognaAkisi(kisa(a.ders_kodu)),
209
+ cikti_yaz: (a) => ciktiAkisi(kisa(a.ders_kodu)),
210
+ ck_pc_hesabi: (a) => ckPcAkisi(kisa(a.ders_kodu)),
211
+ olcut_rehberi: (a) => olcutAkisi(kisa(a.cerceve)),
212
+ dil_denetimi: () => dilAkisi(),
213
+ };
214
+ const akreditMenusu = (odak) => {
215
+ const secilen = ODAKLAR.find(([no, , ad]) => odak === no || odak === ad);
216
+ if (secilen)
217
+ return AKISLAR[secilen[2]]?.({}) ?? "";
218
+ return [
219
+ "Akredit açık araçlarıyla hocaya yardım edeceksin. Önce hangi aracı kullanmak istediğini sor.",
220
+ "Hoca seçmeden hiçbir akışa başlama: OBS girişi isteme, sayfa açma, araç çağırma.",
221
+ "",
222
+ "Hocaya tam olarak şunu göster ve yanıtını bekle:",
223
+ "",
224
+ "Merhaba. Neye odaklanalım? Numarasını yazmanız yeter.",
225
+ ...ODAKLAR.map(([no, ad]) => `${no}) ${ad}`),
226
+ `${ODAKLAR.length + 1}) Başka bir şey: tüm araçları kısaca listele`,
227
+ "",
228
+ `Seçim ${ODAKLAR.length + 1} ise sunucudaki araçları adlarıyla değil, ne işe yaradıklarıyla birer cümlede numaralı listele ve yeniden seçtir.`,
229
+ "Bu ilk menüde 4 bir odaktır (ölçüt rehberi); akıllı doldurma kısayolu odak seçildikten sonraki menülerde geçerlidir.",
230
+ "",
231
+ "Seçime göre aşağıdaki akışlardan ilgili olanı izle:",
232
+ "",
233
+ ...ODAKLAR.map(([no, , ad]) => `Seçim ${no}:\n${AKISLAR[ad]?.({}) ?? ""}\n`),
234
+ ].join("\n");
235
+ };
236
+ const ile = (govde) => `${govde}\n\n${ORTAK_KURALLAR}`;
237
+ export const ACIK_ISTEMLER = [
238
+ {
239
+ ad: "akredit",
240
+ baslik: "Akredit: hangi aracı kullanalım?",
241
+ aciklama: "Akredit ile ilk adım. Önce hangi aracı kullanacağınızı seçersiniz: Bologna paketi, öğrenme çıktısı, ÇK/PÇ hesabı, ölçüt rehberi, dil denetimi ya da başka bir araç.",
242
+ argumanlar: { odak: "İsteğe bağlı: 1-5 arası odak numarası" },
243
+ metin: (a) => ile(akreditMenusu(kisa(a.odak, 20))),
244
+ },
245
+ {
246
+ ad: "bologna_doldur",
247
+ baslik: "Bologna paketini denetle ve doldur",
248
+ aciklama: "OBS'nize kendiniz girersiniz; asistan Bologna sayfanızı okur, boş ve zayıf alanları bulur, hepsini akıllıca doldurup denetler ve tek özet gösterir. Formu doldurur; Kaydet'e siz basarsınız ya da açık izninizle o basar.",
249
+ argumanlar: { ders_kodu: "İsteğe bağlı: ders kodu, ör. KRG101" },
250
+ metin: (a) => ile(bolognaAkisi(kisa(a.ders_kodu))),
251
+ },
252
+ {
253
+ ad: "cikti_yaz",
254
+ baslik: "Öğrenme çıktısı yaz ya da iyileştir",
255
+ aciklama: "Ders öğrenme çıktılarını ölçülebilir fiillerle yazar ya da iyileştirir; hepsini tek adımda yazıp denetler, isterseniz numarayla düzeltirsiniz.",
256
+ argumanlar: { ders_kodu: "İsteğe bağlı: ders kodu" },
257
+ metin: (a) => ile(ciktiAkisi(kisa(a.ders_kodu))),
258
+ },
259
+ {
260
+ ad: "ck_pc_hesabi",
261
+ baslik: "ÇK ve PÇ sağlanma hesabı",
262
+ aciklama: "Excel not tablonuzu yapıştırırsınız; öğrencilere takma ad verilir, ders ve program çıktısı sağlanma düzeyi hesaplanır.",
263
+ argumanlar: { ders_kodu: "İsteğe bağlı: ders kodu" },
264
+ metin: (a) => ile(ckPcAkisi(kisa(a.ders_kodu))),
265
+ },
266
+ {
267
+ ad: "olcut_rehberi",
268
+ baslik: "Ölçüt ve kanıt rehberi",
269
+ aciklama: "YÖKAK, MÜDEK, FEDEK, EPDAD ya da SABAK ölçütünü resmî metniyle bulur, kanıt kontrol listesi çıkarır.",
270
+ argumanlar: { cerceve: "İsteğe bağlı: YÖKAK, MÜDEK, FEDEK, EPDAD ya da SABAK" },
271
+ metin: (a) => ile(olcutAkisi(kisa(a.cerceve, 20))),
272
+ },
273
+ {
274
+ ad: "dil_denetimi",
275
+ baslik: "Öz değerlendirme dil denetimi",
276
+ aciklama: "Rapor metnindeki kanıtsız üstünlük, belirsiz özne gibi sorunları bulur, hepsini düzeltip yeniden denetler ve tek özet gösterir.",
277
+ argumanlar: {},
278
+ metin: () => ile(dilAkisi()),
279
+ },
280
+ ];
281
+ /** Kısa kurallar: akredit_basla yanıtında (istem desteklemeyen istemciler için) yer alır. */
282
+ export const KISA_KURALLAR = [
283
+ "Hoca seçmeden hiçbir akışa başlama; menüyü aynen göster ve numara bekle.",
284
+ "Kısa, sade Türkçe yaz; araç adı, JSON ve teknik terim gösterme. Her mesajda tek iş iste.",
285
+ "Uydurma yok: kişi, kitap, ISBN, DOI, tarih, sayı uydurma. Bilinmeyen EKSİK kalır.",
286
+ "Değerlendirme yöntem ve yüzdeleri, uygulama/lab, kullanılan kaynaklar ve sayfada yoksa amaç/içerik sayfadan okunur; yoksa hocaya TEK mesajda, numaralı ve varsayılan seçenekli sorulur.",
287
+ "Ders adından ürettiğin her şeyi 'öneri' etiketiyle göster. Araç sonucu 'biçim denetiminden geçti; içerik teyidi: hoca' diye sunulur.",
288
+ "Sayıları yalnız araç yanıtından al.",
289
+ "Kaynak yalnız 'katalogda_bulundu' ise öneri olarak, ya da hoca kullandığını söylerse 'hoca_beyani' olarak yazılır.",
290
+ "Giriş bilgisi (parola, doğrulama kodu, çerez) istenmez; hoca yazarsa kullanma, araçlara gönderme.",
291
+ "OBS'ye yazmadan önce alan alan önceki/yeni değer özeti göster; hoca bir kez 'kaydet' der. Kayıttan sonra geri oku ve obs_karsilastir ile doğrula. Gönder düğmesine hoca basar.",
292
+ "Öğrenci adı ya da numarası sohbete yapıştırılmaz; not tablosunda yalnız not sütunları olur.",
293
+ ];
294
+ /** akredit_basla aracının yanıtı: menü, kısa kurallar ve her seçimin akışı (istem desteklemeyen istemciler için). */
295
+ export function baslangic() {
296
+ return {
297
+ hocayaGoster: [
298
+ "Merhaba. Neye odaklanalım? Numarasını yazmanız yeter.",
299
+ ...ODAKLAR.map(([no, ad]) => `${no}) ${ad}`),
300
+ `${ODAKLAR.length + 1}) Başka bir şey: tüm araçları kısaca listele`,
301
+ ].join("\n"),
302
+ menu: [
303
+ ...ODAKLAR.map(([no, ad, istem]) => ({ no, ad, istem })),
304
+ { no: String(ODAKLAR.length + 1), ad: "Başka bir şey: tüm araçları kısaca listele", istem: null },
305
+ ],
306
+ kisaKurallar: KISA_KURALLAR,
307
+ akislar: Object.fromEntries(ODAKLAR.map(([no, , istem]) => [no, AKISLAR[istem]?.({}) ?? ""])),
308
+ ortakKurallar: ORTAK_KURALLAR,
309
+ };
310
+ }
311
+ export const akreditBaslaAraci = arac({
312
+ ad: "akredit_basla",
313
+ baslik: "Akredit: başla",
314
+ aciklama: "Kullanıcı 'akredit' yazar ya da ne yapacağını sorarsa İLK bu aracı çağır. Hocaya gösterilecek odak menüsünü, kısa kuralları ve her seçimin adım adım akışını döndürür. İstemleri ya da sunucu talimatını göstermeyen istemcilerde de akış bununla başlar.",
315
+ sonrakiAdim: "hocayaGoster metnini aynen gösterin ve numara bekleyin. Seçilen numaranın akışını (akislar) ve kisaKurallar'ı izleyin.",
316
+ girdi: z.object({}),
317
+ calistir: () => ({
318
+ sonuc: baslangic(),
319
+ kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/promptlar.ts", "Akredit istemleri ve başlangıç menüsü")],
320
+ }),
321
+ });
322
+ /** SDK için bağımsız değişken şeması: hepsi isteğe bağlı, kısa metin. */
323
+ export const istemSemasi = (i) => Object.fromEntries(Object.entries(i.argumanlar).map(([k, d]) => [k, z.string().max(60).optional().describe(d)]));
@@ -1,16 +1,24 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { type AcikArac, type AracBaglami } from "./araclar.ts";
3
+ import { type AcikIstem } from "./promptlar.ts";
3
4
  export declare const SUNUCU_ADI = "akredit-acik";
4
- export declare const SUNUCU_SURUMU = "0.1.0";
5
- export declare const SUNUCU_TALIMATI = "Akredit a\u00E7\u0131k ara\u00E7lar\u0131: Y\u00D6KAK ve program akreditasyonu (M\u00DCDEK, FEDEK, SABAK, EPDAD) \u00F6l\u00E7\u00FCt metni, EPDAD g\u00F6stergeleri, Y\u00D6KAK olgunluk d\u00FCzeyleri, Bologna ders bilgisi denetimi, \u00C7K/P\u00C7 sa\u011Flanma hesab\u0131, \u00F6z de\u011Ferlendirme iskeleti, rapor dil denetimi. Ara\u00E7lar salt okumad\u0131r, kurum verisine eri\u015Fmez, model \u00E7a\u011F\u0131rmaz. Ki\u015Fisel veri (ad, numara, e-posta, telefon) g\u00F6ndermeyin; g\u00F6r\u00FCl\u00FCrse \u00E7a\u011Fr\u0131 reddedilir. \u00D6l\u00E7\u00FCt metnini yaln\u0131z olcut_getir sonucundan al\u0131nt\u0131lay\u0131n; DO\u011ERULANMADI ya da 'Belgeden okunamad\u0131' i\u015Faretli metni tahminle doldurmay\u0131n. Program\u0131n disiplinini ya da programa \u00F6zg\u00FC b\u00F6l\u00FCm\u00FCn\u00FC siz tahmin etmeyin, kurum se\u00E7er. Say\u0131lar\u0131 yaln\u0131z ara\u00E7 sonucundan al\u0131n. \u00D6z de\u011Ferlendirme iskeleti metin i\u00E7ermez: kan\u0131t\u0131 olmayan c\u00FCmleyi EKS\u0130K b\u0131rak\u0131n. Akredit'in Y\u00D6KAK ile ba\u011F\u0131 yoktur.";
5
+ export declare const SUNUCU_SURUMU = "0.5.0";
6
+ export declare const SUNUCU_TALIMATI: string;
6
7
  export interface AcikSunucuSecenekleri {
7
8
  simdi?: () => Date;
8
9
  araclar?: readonly AcikArac[];
10
+ istemler?: readonly AcikIstem[];
11
+ /** kaynak_dogrula için fetch (test). */
12
+ fetch?: typeof fetch;
9
13
  }
10
- /** Bir aracı doğrudan çalıştırır (MCP katmanı dışında da kullanılabilir: test, belge). */
11
- export declare function aracCalistir(a: AcikArac, girdi: unknown, b: AracBaglami): {
14
+ type Sonuc = {
12
15
  veri: Record<string, unknown>;
13
16
  hata: boolean;
14
17
  };
18
+ /** Bir aracı doğrudan çalıştırır (MCP katmanı dışında da kullanılabilir: test, belge). Yalnız eşzamanlı araçlar. */
19
+ export declare function aracCalistir(a: AcikArac, girdi: unknown, b: AracBaglami): Sonuc;
20
+ /** Her araç için (eşzamanlı ya da eşzamansız) çalıştırma; MCP katmanı bunu kullanır. */
21
+ export declare function aracCalistirAsenkron(a: AcikArac, girdi: unknown, b: AracBaglami): Promise<Sonuc>;
15
22
  /** Yeni bir McpServer kurar. Durumsuz HTTP'de her istek için, stdio'da süreç başına bir kez çağrılır. */
16
23
  export declare function acikSunucu(s?: AcikSunucuSecenekleri): McpServer;
24
+ export {};
@@ -2,44 +2,114 @@
2
2
  // deterministiktir; araç açıklamaları ve yanıtlar Türkçedir. Gelen veride kişisel veri deseni görülürse
3
3
  // çağrı hesap yapılmadan reddedilir. Her yanıt kaynak listesi ve "YÖKAK ile bağı yoktur" notu taşır.
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { ACIK_ARACLAR, AKREDIT_NOTU, AracHatasi, DEPO } from "./araclar.js";
6
- import { kisiselVeriIletisi, kisiselVeriTurleri } from "./kisisel-veri.js";
5
+ import { ACIK_ARACLAR, AKREDIT_NOTU, AracHatasi, DEPO, } from "./araclar.js";
6
+ import { GIRIS_BILGISI_ILETISI, girisBilgisiVar, kisiselVeriIletisi, kisiselVeriTurleri } from "./kisisel-veri.js";
7
+ import { ACIK_ISTEMLER, istemSemasi } from "./promptlar.js";
7
8
  export const SUNUCU_ADI = "akredit-acik";
8
- export const SUNUCU_SURUMU = "0.1.0";
9
- export const SUNUCU_TALIMATI = "Akredit açık araçları: YÖKAK ve program akreditasyonu (MÜDEK, FEDEK, SABAK, EPDAD) ölçüt metni, EPDAD göstergeleri, YÖKAK olgunluk düzeyleri, Bologna ders bilgisi denetimi, ÇK/PÇ sağlanma hesabı, öz değerlendirme iskeleti, rapor dil denetimi. Araçlar salt okumadır, kurum verisine erişmez, model çağırmaz. Kişisel veri (ad, numara, e-posta, telefon) göndermeyin; görülürse çağrı reddedilir. Ölçüt metnini yalnız olcut_getir sonucundan alıntılayın; DOĞRULANMADI ya da 'Belgeden okunamadı' işaretli metni tahminle doldurmayın. Programın disiplinini ya da programa özgü bölümünü siz tahmin etmeyin, kurum seçer. Sayıları yalnız araç sonucundan alın. Öz değerlendirme iskeleti metin içermez: kanıtı olmayan cümleyi EKSİK bırakın. Akredit'in YÖKAK ile bağı yoktur.";
9
+ export const SUNUCU_SURUMU = "0.5.0";
10
+ export const SUNUCU_TALIMATI = [
11
+ "Akredit açık araçları öğretim elemanlarına kalite ve akreditasyon işlerinde yardım eder: Bologna ders paketi denetimi ve doldurma, öğrenme çıktısı yazma, ÇK/PÇ hesabı, YÖKAK ve program akreditasyonu (MÜDEK, FEDEK, SABAK, EPDAD) ölçüt ve kanıt rehberi, rapor dil denetimi, kaynak doğrulama.",
12
+ "Hoca ne yapacağını söylemediyse ya da 'akredit' yazarsa İLK akredit_basla aracını çağır, menüyü göster ve önce hangi aracı kullanmak istediğini sor.",
13
+ "Amaç: hocanın en az müdahalesiyle işi hızlı ama denetimli ve teyitli bitirmek. Her seçim menüsünde son seçenek '4) Hepsini akıllıca doldur (önerilen)' olur: her alan için en iyi seçeneği sen seçersin, araçla denetlersin, geçmeyeni düzeltip yeniden denetlersin, sonunda tek özet gösterip değiştirmek istediği numarayı sorarsın.",
14
+ "Yalnız hocanın bilebileceği ve resmî taahhüt olan bilgiler (değerlendirme yöntem ve yüzdeleri, uygulama/lab, kullanılan kaynaklar, sayfada yoksa amaç ve içerik) sayfadan okunur; yoksa hocaya TEK mesajda, numaralı ve varsayılan seçenekli sorulur, uydurulmaz. Ders adından üretilen her şey 'öneri' etiketiyle gösterilir. Araç sonucu 'biçim denetiminden geçti; içerik teyidi: hoca' diye sunulur.",
15
+ "Giriş bilgisi: hocadan kullanıcı adı, parola, doğrulama kodu ya da çerez asla isteme. Sunucu bunları istemez; görülürse çağrıyı reddeder ve kaydetmez. Hoca yazarsa kullanma, araçlara gönderme, silmesini ve parolasını değiştirmesini öner. Hoca giriş bilgisini yalnız OBS'nin kendi ekranına kendisi yazar. CAPTCHA'yı hoca çözer.",
16
+ "OBS'ye yazma: önce alan alan önceki ve yeni değer özetini göster, hoca bir kez 'kaydet' der; ancak ondan sonra Kaydet'e basarsın. Kayıttan sonra sayfayı geri okuyup obs_karsilastir ile doğrula; ders kodu ya da dönem değişmişse yazmayı durdur. Doğrulanmamış kaynak ve teyitsiz değerlendirme yüzdesi yazılmaz. Onaya Gönder düğmesine hoca basar.",
17
+ "Tarayıcı aracın varsa OBS sayfasını okuyup formu doldurabilirsin; yoksa ya da engel çıkarsa engel_coz ve kurulum_yardimi araçlarının adımlarını hocaya sırayla, tek tek göster. Tarayıcı yoksa OBS'ye yazma alan alan kopyalamaya düşer.",
18
+ "Sunucu model çağırmaz ve metin üretmez: seçenekleri sen yazarsın, sunucu kural ve kalıp verir, denetler ve sayıları hesaplar. Sayıları yalnız araç sonucundan al.",
19
+ "Bologna sayfasındaki Dersi Verenler, Yardımcılar ve Koordinatör satırları yayımlanmış görev bilgisidir: olduğu gibi okunur, korunur, değiştirilmez. Öğrenci adı ve numarası sohbete yapıştırılmaz: not tablosunda yalnız not sütunları olur; kimlik sütunu görülürse tablo işlenmez. Sunucu istek içeriğini günlüğe yazmaz.",
20
+ "Kaynak: önce sayfadaki ve hocanın kullandığı kaynaklar. Öneri gerekirse kendi web arama aracınla aday bul, kaynak_dogrula ile sorgula, yalnız 'katalogda_bulundu' dönenleri künyesi ve bağlantısıyla 'öneri' olarak göster; uygunluğuna hoca karar verir. Katalogda bulunmayanı kendin önerme; hoca kullandığını söylerse 'hoca_beyani' olarak yaz. Ölçüt metnini yalnız olcut_getir sonucundan alıntılayın; DOĞRULANMADI ya da 'Belgeden okunamadı' işaretli metni tahminle doldurmayın. Programın disiplinini kurum seçer. Kanıtı olmayan cümle EKSİK kalır. Kitap, kişi, tarih uydurmayın.",
21
+ "Sunucu kullanıcıdan adres çekmez: Bologna sayfasını hoca yapıştırır ya da siz kendi web aracınızla okursunuz. Tek dış istek kaynak_dogrula'nın sabit katalog adresleridir (Crossref, Open Library, Google Books). Onay ve dış kuruma gönderim hocadadır. Akredit'in YÖKAK ile bağı yoktur.",
22
+ ].join(" ");
10
23
  const metinSonuc = (veri, isError = false) => ({
11
24
  content: [{ type: "text", text: JSON.stringify(veri, null, 2) }],
12
25
  structuredContent: veri,
13
26
  ...(isError ? { isError: true } : {}),
14
27
  });
15
- /** Bir aracı doğrudan çalıştırır (MCP katmanı dışında da kullanılabilir: test, belge). */
16
- export function aracCalistir(a, girdi, b) {
17
- const turler = kisiselVeriTurleri(girdi);
28
+ /** Denetimler (giriş bilgisi, kişisel veri, şema). Geçerse doğrulanmış girdiyi, geçmezse hata sonucunu döndürür. */
29
+ function hazirla(a, girdi) {
30
+ const ayik = a.onAyikla ? a.onAyikla(girdi) : { girdi, atilan: [], ileti: undefined };
31
+ girdi = ayik.girdi;
32
+ if (girisBilgisiVar(girdi))
33
+ return { hata: { veri: { hata: GIRIS_BILGISI_ILETISI, kod: "giris_bilgisi", not: AKREDIT_NOTU }, hata: true } };
34
+ const red = a.onDenetim?.(girdi);
35
+ if (red)
36
+ return { hata: { veri: { hata: red, kod: "kisisel_veri", not: AKREDIT_NOTU }, hata: true } };
37
+ const turler = kisiselVeriTurleri(girdi, a.kunyeAlanlari, a.kisiselVeriKapsami ?? "ogrenci");
18
38
  if (turler.length)
19
- return { veri: { hata: kisiselVeriIletisi(turler), kod: "kisisel_veri", not: AKREDIT_NOTU }, hata: true };
39
+ return {
40
+ hata: {
41
+ veri: {
42
+ hata: [kisiselVeriIletisi(turler), a.kisiselVeriIpucu].filter(Boolean).join(" "),
43
+ kod: "kisisel_veri",
44
+ not: AKREDIT_NOTU,
45
+ },
46
+ hata: true,
47
+ },
48
+ };
20
49
  const g = a.girdi.safeParse(girdi);
21
50
  if (!g.success) {
22
51
  const alanlar = g.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "girdi"}: ${i.message}`);
23
52
  return {
24
- veri: {
25
- hata: "Girdi geçersiz. Alanları düzeltip yeniden deneyin.",
26
- kod: "gecersiz_girdi",
27
- alanlar,
28
- not: AKREDIT_NOTU,
53
+ hata: {
54
+ veri: {
55
+ hata: "Girdi geçersiz. Aşağıdaki alanları düzeltip yeniden deneyin.",
56
+ kod: "gecersiz_girdi",
57
+ alanlar,
58
+ not: AKREDIT_NOTU,
59
+ },
60
+ hata: true,
29
61
  },
30
- hata: true,
31
62
  };
32
63
  }
64
+ return { veri: g.data, atilan: ayik.atilan, ileti: ayik.ileti };
65
+ }
66
+ function basari(a, s, atilan, ileti) {
67
+ return {
68
+ veri: {
69
+ sonuc: s.sonuc,
70
+ sonrakiAdim: s.sonrakiAdim ?? a.sonrakiAdim,
71
+ ...(atilan.length
72
+ ? { atilan: ileti ?? `Şu kısımlar okunmadan atıldı, hiçbir yere yazılmadı: ${atilan.join(", ")}.` }
73
+ : {}),
74
+ kaynak: s.kaynak,
75
+ not: AKREDIT_NOTU,
76
+ kod_deposu: DEPO,
77
+ },
78
+ hata: false,
79
+ };
80
+ }
81
+ function hataSonucu(e) {
82
+ const ileti = e instanceof AracHatasi ? e.message : "Beklenmeyen bir sorun oluştu. Girdiyi denetleyip yeniden deneyin.";
83
+ return {
84
+ veri: { hata: ileti, kod: e instanceof AracHatasi ? "arac_hatasi" : "beklenmeyen", not: AKREDIT_NOTU },
85
+ hata: true,
86
+ };
87
+ }
88
+ /** Bir aracı doğrudan çalıştırır (MCP katmanı dışında da kullanılabilir: test, belge). Yalnız eşzamanlı araçlar. */
89
+ export function aracCalistir(a, girdi, b) {
90
+ const h = hazirla(a, girdi);
91
+ if ("hata" in h)
92
+ return h.hata;
33
93
  try {
34
- const s = a.calistir(g.data, b);
35
- return { veri: { sonuc: s.sonuc, kaynak: s.kaynak, not: AKREDIT_NOTU, kod_deposu: DEPO }, hata: false };
94
+ const s = a.calistir(h.veri, b);
95
+ if (s instanceof Promise)
96
+ throw new Error("Eşzamansız araç için aracCalistirAsenkron kullanın.");
97
+ return basari(a, s, h.atilan, h.ileti);
36
98
  }
37
99
  catch (e) {
38
- const ileti = e instanceof AracHatasi ? e.message : "Beklenmeyen bir sorun oluştu. Girdiyi denetleyip yeniden deneyin.";
39
- return {
40
- veri: { hata: ileti, kod: e instanceof AracHatasi ? "arac_hatasi" : "beklenmeyen", not: AKREDIT_NOTU },
41
- hata: true,
42
- };
100
+ return hataSonucu(e);
101
+ }
102
+ }
103
+ /** Her araç için (eşzamanlı ya da eşzamansız) çalıştırma; MCP katmanı bunu kullanır. */
104
+ export async function aracCalistirAsenkron(a, girdi, b) {
105
+ const h = hazirla(a, girdi);
106
+ if ("hata" in h)
107
+ return h.hata;
108
+ try {
109
+ return basari(a, await a.calistir(h.veri, b), h.atilan, h.ileti);
110
+ }
111
+ catch (e) {
112
+ return hataSonucu(e);
43
113
  }
44
114
  }
45
115
  /** Yeni bir McpServer kurar. Durumsuz HTTP'de her istek için, stdio'da süreç başına bir kez çağrılır. */
@@ -50,7 +120,10 @@ export function acikSunucu(s = {}) {
50
120
  title: "Akredit (açık araçlar)",
51
121
  version: SUNUCU_SURUMU,
52
122
  websiteUrl: "https://akredit.org/mcp-kullan",
53
- }, { instructions: SUNUCU_TALIMATI, capabilities: { tools: { listChanged: false } } });
123
+ }, {
124
+ instructions: SUNUCU_TALIMATI,
125
+ capabilities: { tools: { listChanged: false }, prompts: { listChanged: false } },
126
+ });
54
127
  for (const a of s.araclar ?? ACIK_ARACLAR) {
55
128
  sunucu.registerTool(a.ad, {
56
129
  title: a.baslik,
@@ -58,11 +131,22 @@ export function acikSunucu(s = {}) {
58
131
  // Şema SDK'ya yalnız tanıtım (JSON şeması) içindir; asıl doğrulama aracCalistir içinde, kişisel veri
59
132
  // denetiminden sonra yapılır. Bu yüzden girdi gevşek (loose) verilir.
60
133
  inputSchema: a.girdi.loose(),
61
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
134
+ annotations: {
135
+ readOnlyHint: true,
136
+ destructiveHint: false,
137
+ idempotentHint: true,
138
+ openWorldHint: a.disAg === true,
139
+ },
62
140
  }, async (girdi) => {
63
- const r = aracCalistir(a, girdi, { simdi });
141
+ const r = await aracCalistirAsenkron(a, girdi, { simdi, ...(s.fetch ? { fetch: s.fetch } : {}) });
64
142
  return metinSonuc(r.veri, r.hata);
65
143
  });
66
144
  }
145
+ for (const i of s.istemler ?? ACIK_ISTEMLER) {
146
+ sunucu.registerPrompt(i.ad, { title: i.baslik, description: i.aciklama, argsSchema: istemSemasi(i) }, (a) => ({
147
+ description: i.aciklama,
148
+ messages: [{ role: "user", content: { type: "text", text: i.metin(a) } }],
149
+ }));
150
+ }
67
151
  return sunucu;
68
152
  }
@@ -0,0 +1,59 @@
1
+ import type { z } from "zod";
2
+ import type { KisiselVeriKapsami } from "./kisisel-veri.ts";
3
+ export declare const DEPO = "https://github.com/tialkan/akredit";
4
+ /** Her yanıtta yer alan not. */
5
+ export declare const AKREDIT_NOTU = "Akredit ba\u011F\u0131ms\u0131z, a\u00E7\u0131k kaynak bir ara\u00E7t\u0131r; Y\u00D6KAK ile ba\u011F\u0131 yoktur. Sonu\u00E7lar resm\u00EE de\u011Ferlendirme yerine ge\u00E7mez; beyan ve karar kurumdad\u0131r.";
6
+ export interface Kaynak {
7
+ ad: string;
8
+ adres: string;
9
+ sayfa?: number | null;
10
+ sha256?: string | null;
11
+ erisimTarihi?: string;
12
+ }
13
+ export interface AracSonucu {
14
+ sonuc: unknown;
15
+ kaynak: Kaynak[];
16
+ /** Asistana bir sonraki adım önerisi; verilmezse aracın varsayılanı kullanılır. */
17
+ sonrakiAdim?: string;
18
+ }
19
+ export interface AracBaglami {
20
+ simdi: () => Date;
21
+ /** Dış istek için fetch; testlerde sahtesi verilir. Verilmezse genel fetch kullanılır. */
22
+ fetch?: typeof fetch;
23
+ }
24
+ export interface AcikArac<S extends z.ZodObject = z.ZodObject> {
25
+ ad: string;
26
+ baslik: string;
27
+ aciklama: string;
28
+ girdi: S;
29
+ /** Yanıtta dönen varsayılan "sonraki adım" önerisi. */
30
+ sonrakiAdim: string;
31
+ /** Kişisel veri reddinde iletiye eklenecek, bu araca özgü ipucu. */
32
+ kisiselVeriIpucu?: string;
33
+ /**
34
+ * Kişisel veri denetiminden önce, yalnız bellekte çalışan ayıklama. Atılan değerler hiçbir yere yazılmaz;
35
+ * yalnız atılan alanların adları döner. Ayıklamadan sonra kalan girdi yine denetlenir.
36
+ */
37
+ onAyikla?: (girdi: unknown) => {
38
+ girdi: unknown;
39
+ atilan: string[];
40
+ ileti?: string;
41
+ };
42
+ /**
43
+ * Kişisel veri taramasından önce çalışan red denetimi. İleti dönerse çağrı işlenmeden "kisisel_veri" koduyla
44
+ * reddedilir; değerler hiçbir yere yazılmaz.
45
+ */
46
+ onDenetim?: (girdi: unknown) => string | null;
47
+ /** Kişisel veri denetiminin kapsamı; varsayılan "ogrenci" (bkz. kisisel-veri.ts). */
48
+ kisiselVeriKapsami?: KisiselVeriKapsami;
49
+ /** Araç dış ağa (yalnız izinli hostlara) istek atar mı. */
50
+ disAg?: boolean;
51
+ /** Künye kuralıyla denetlenen alan adları: altındaki yazar adları kişisel veri sayılmaz (e-posta, telefon yine sayılır). */
52
+ kunyeAlanlari?: readonly string[];
53
+ calistir: (girdi: z.infer<S>, b: AracBaglami) => AracSonucu | Promise<AracSonucu>;
54
+ }
55
+ export declare const arac: <S extends z.ZodObject>(a: AcikArac<S>) => AcikArac<S>;
56
+ /** Araç girdisi hatası: kullanıcıya gösterilecek Türkçe ileti. */
57
+ export declare class AracHatasi extends Error {
58
+ }
59
+ export declare const KOD_KAYNAGI: (yol: string, ad: string) => Kaynak;
@@ -0,0 +1,10 @@
1
+ // Araçların ortak yapı taşları: araç tanımı, hata türü, kaynak biçimi ve her yanıttaki not.
2
+ // Ayrı dosyadadır, çünkü hem ölçüt araçları (araclar.ts) hem Bologna araçları (bologna.ts) kullanır.
3
+ export const DEPO = "https://github.com/tialkan/akredit";
4
+ /** Her yanıtta yer alan not. */
5
+ export const AKREDIT_NOTU = "Akredit bağımsız, açık kaynak bir araçtır; YÖKAK ile bağı yoktur. Sonuçlar resmî değerlendirme yerine geçmez; beyan ve karar kurumdadır.";
6
+ export const arac = (a) => a;
7
+ /** Araç girdisi hatası: kullanıcıya gösterilecek Türkçe ileti. */
8
+ export class AracHatasi extends Error {
9
+ }
10
+ export const KOD_KAYNAGI = (yol, ad) => ({ ad, adres: `${DEPO}/tree/main/${yol}` });