@akredit/acik-mcp 0.1.0 → 0.4.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 (29) 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 +27 -114
  4. package/dist/apps/acik-mcp/src/bologna.d.ts +209 -0
  5. package/dist/apps/acik-mcp/src/bologna.js +1321 -0
  6. package/dist/apps/acik-mcp/src/genel-ayristir.d.ts +83 -0
  7. package/dist/apps/acik-mcp/src/genel-ayristir.js +411 -0
  8. package/dist/apps/acik-mcp/src/http.js +4 -2
  9. package/dist/apps/acik-mcp/src/index.d.ts +6 -0
  10. package/dist/apps/acik-mcp/src/index.js +6 -0
  11. package/dist/apps/acik-mcp/src/kaynak-dogrula.d.ts +47 -0
  12. package/dist/apps/acik-mcp/src/kaynak-dogrula.js +319 -0
  13. package/dist/apps/acik-mcp/src/kaynak.d.ts +72 -0
  14. package/dist/apps/acik-mcp/src/kaynak.js +155 -0
  15. package/dist/apps/acik-mcp/src/kisisel-veri.d.ts +14 -2
  16. package/dist/apps/acik-mcp/src/kisisel-veri.js +63 -15
  17. package/dist/apps/acik-mcp/src/not-tablosu.d.ts +33 -0
  18. package/dist/apps/acik-mcp/src/not-tablosu.js +243 -0
  19. package/dist/apps/acik-mcp/src/promptlar.d.ts +25 -0
  20. package/dist/apps/acik-mcp/src/promptlar.js +253 -0
  21. package/dist/apps/acik-mcp/src/sunucu.d.ts +12 -4
  22. package/dist/apps/acik-mcp/src/sunucu.js +103 -24
  23. package/dist/apps/acik-mcp/src/temel.d.ts +54 -0
  24. package/dist/apps/acik-mcp/src/temel.js +10 -0
  25. package/dist/apps/acik-mcp/src/yardim.d.ts +56 -0
  26. package/dist/apps/acik-mcp/src/yardim.js +371 -0
  27. package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.d.ts +29 -0
  28. package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.js +200 -0
  29. package/package.json +4 -2
@@ -2,44 +2,109 @@
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.4.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 'akredit' istemindeki araç menüsünü 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
+ "Giriş bilgisi: hocadan kullanıcı adı, parola, doğrulama kodu ya da çerez asla isteme; 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. OBS'de Kaydet'e yalnız hocanın açık izniyle basarsın; onaya Gönder düğmesine hoca basar.",
15
+ "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.",
16
+ "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.",
17
+ "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ı içeren not tabloları takma adla işlenir. Sunucu istek içeriğini günlüğe yazmaz.",
18
+ "Kaynak önerirken hocaya kitap adını sorma: kendi web arama aracınla ders içeriğine uygun eserleri bul, kaynak_dogrula ile doğrula, yalnız doğrulananları künyesi ve bağlantısıyla göster. Doğrulanamayan kaynağı asla önerme. Ö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.",
19
+ "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.",
20
+ ].join(" ");
10
21
  const metinSonuc = (veri, isError = false) => ({
11
22
  content: [{ type: "text", text: JSON.stringify(veri, null, 2) }],
12
23
  structuredContent: veri,
13
24
  ...(isError ? { isError: true } : {}),
14
25
  });
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);
26
+ /** Denetimler (giriş bilgisi, kişisel veri, şema). Geçerse doğrulanmış girdiyi, geçmezse hata sonucunu döndürür. */
27
+ function hazirla(a, girdi) {
28
+ const ayik = a.onAyikla ? a.onAyikla(girdi) : { girdi, atilan: [], ileti: undefined };
29
+ girdi = ayik.girdi;
30
+ if (girisBilgisiVar(girdi))
31
+ return { hata: { veri: { hata: GIRIS_BILGISI_ILETISI, kod: "giris_bilgisi", not: AKREDIT_NOTU }, hata: true } };
32
+ const turler = kisiselVeriTurleri(girdi, a.kunyeAlanlari, a.kisiselVeriKapsami ?? "ogrenci");
18
33
  if (turler.length)
19
- return { veri: { hata: kisiselVeriIletisi(turler), kod: "kisisel_veri", not: AKREDIT_NOTU }, hata: true };
34
+ return {
35
+ hata: {
36
+ veri: {
37
+ hata: [kisiselVeriIletisi(turler), a.kisiselVeriIpucu].filter(Boolean).join(" "),
38
+ kod: "kisisel_veri",
39
+ not: AKREDIT_NOTU,
40
+ },
41
+ hata: true,
42
+ },
43
+ };
20
44
  const g = a.girdi.safeParse(girdi);
21
45
  if (!g.success) {
22
46
  const alanlar = g.error.issues.slice(0, 5).map((i) => `${i.path.join(".") || "girdi"}: ${i.message}`);
23
47
  return {
24
- veri: {
25
- hata: "Girdi geçersiz. Alanları düzeltip yeniden deneyin.",
26
- kod: "gecersiz_girdi",
27
- alanlar,
28
- not: AKREDIT_NOTU,
48
+ hata: {
49
+ veri: {
50
+ hata: "Girdi geçersiz. Aşağıdaki alanları düzeltip yeniden deneyin.",
51
+ kod: "gecersiz_girdi",
52
+ alanlar,
53
+ not: AKREDIT_NOTU,
54
+ },
55
+ hata: true,
29
56
  },
30
- hata: true,
31
57
  };
32
58
  }
59
+ return { veri: g.data, atilan: ayik.atilan, ileti: ayik.ileti };
60
+ }
61
+ function basari(a, s, atilan, ileti) {
62
+ return {
63
+ veri: {
64
+ sonuc: s.sonuc,
65
+ sonrakiAdim: s.sonrakiAdim ?? a.sonrakiAdim,
66
+ ...(atilan.length
67
+ ? { atilan: ileti ?? `Şu kısımlar okunmadan atıldı, hiçbir yere yazılmadı: ${atilan.join(", ")}.` }
68
+ : {}),
69
+ kaynak: s.kaynak,
70
+ not: AKREDIT_NOTU,
71
+ kod_deposu: DEPO,
72
+ },
73
+ hata: false,
74
+ };
75
+ }
76
+ function hataSonucu(e) {
77
+ const ileti = e instanceof AracHatasi ? e.message : "Beklenmeyen bir sorun oluştu. Girdiyi denetleyip yeniden deneyin.";
78
+ return {
79
+ veri: { hata: ileti, kod: e instanceof AracHatasi ? "arac_hatasi" : "beklenmeyen", not: AKREDIT_NOTU },
80
+ hata: true,
81
+ };
82
+ }
83
+ /** Bir aracı doğrudan çalıştırır (MCP katmanı dışında da kullanılabilir: test, belge). Yalnız eşzamanlı araçlar. */
84
+ export function aracCalistir(a, girdi, b) {
85
+ const h = hazirla(a, girdi);
86
+ if ("hata" in h)
87
+ return h.hata;
33
88
  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 };
89
+ const s = a.calistir(h.veri, b);
90
+ if (s instanceof Promise)
91
+ throw new Error("Eşzamansız araç için aracCalistirAsenkron kullanın.");
92
+ return basari(a, s, h.atilan, h.ileti);
36
93
  }
37
94
  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
- };
95
+ return hataSonucu(e);
96
+ }
97
+ }
98
+ /** Her araç için (eşzamanlı ya da eşzamansız) çalıştırma; MCP katmanı bunu kullanır. */
99
+ export async function aracCalistirAsenkron(a, girdi, b) {
100
+ const h = hazirla(a, girdi);
101
+ if ("hata" in h)
102
+ return h.hata;
103
+ try {
104
+ return basari(a, await a.calistir(h.veri, b), h.atilan, h.ileti);
105
+ }
106
+ catch (e) {
107
+ return hataSonucu(e);
43
108
  }
44
109
  }
45
110
  /** 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 +115,10 @@ export function acikSunucu(s = {}) {
50
115
  title: "Akredit (açık araçlar)",
51
116
  version: SUNUCU_SURUMU,
52
117
  websiteUrl: "https://akredit.org/mcp-kullan",
53
- }, { instructions: SUNUCU_TALIMATI, capabilities: { tools: { listChanged: false } } });
118
+ }, {
119
+ instructions: SUNUCU_TALIMATI,
120
+ capabilities: { tools: { listChanged: false }, prompts: { listChanged: false } },
121
+ });
54
122
  for (const a of s.araclar ?? ACIK_ARACLAR) {
55
123
  sunucu.registerTool(a.ad, {
56
124
  title: a.baslik,
@@ -58,11 +126,22 @@ export function acikSunucu(s = {}) {
58
126
  // Şema SDK'ya yalnız tanıtım (JSON şeması) içindir; asıl doğrulama aracCalistir içinde, kişisel veri
59
127
  // denetiminden sonra yapılır. Bu yüzden girdi gevşek (loose) verilir.
60
128
  inputSchema: a.girdi.loose(),
61
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
129
+ annotations: {
130
+ readOnlyHint: true,
131
+ destructiveHint: false,
132
+ idempotentHint: true,
133
+ openWorldHint: a.disAg === true,
134
+ },
62
135
  }, async (girdi) => {
63
- const r = aracCalistir(a, girdi, { simdi });
136
+ const r = await aracCalistirAsenkron(a, girdi, { simdi, ...(s.fetch ? { fetch: s.fetch } : {}) });
64
137
  return metinSonuc(r.veri, r.hata);
65
138
  });
66
139
  }
140
+ for (const i of s.istemler ?? ACIK_ISTEMLER) {
141
+ sunucu.registerPrompt(i.ad, { title: i.baslik, description: i.aciklama, argsSchema: istemSemasi(i) }, (a) => ({
142
+ description: i.aciklama,
143
+ messages: [{ role: "user", content: { type: "text", text: i.metin(a) } }],
144
+ }));
145
+ }
67
146
  return sunucu;
68
147
  }
@@ -0,0 +1,54 @@
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
+ /** Kişisel veri denetiminin kapsamı; varsayılan "ogrenci" (bkz. kisisel-veri.ts). */
43
+ kisiselVeriKapsami?: KisiselVeriKapsami;
44
+ /** Araç dış ağa (yalnız izinli hostlara) istek atar mı. */
45
+ disAg?: boolean;
46
+ /** Künye kuralıyla denetlenen alan adları: altındaki yazar adları kişisel veri sayılmaz (e-posta, telefon yine sayılır). */
47
+ kunyeAlanlari?: readonly string[];
48
+ calistir: (girdi: z.infer<S>, b: AracBaglami) => AracSonucu | Promise<AracSonucu>;
49
+ }
50
+ export declare const arac: <S extends z.ZodObject>(a: AcikArac<S>) => AcikArac<S>;
51
+ /** Araç girdisi hatası: kullanıcıya gösterilecek Türkçe ileti. */
52
+ export declare class AracHatasi extends Error {
53
+ }
54
+ 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}` });
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ export interface Adim {
3
+ no: number;
4
+ yap: string;
5
+ /** Adımdan sonra ekranda ne görüleceği. */
6
+ goreceginiz?: string;
7
+ /** Menü ya da düğme adı sürüme göre değişebilir, doğrulanmadı. */
8
+ degisebilir?: boolean;
9
+ }
10
+ /** Her rehberin başında hocaya söylenecek giriş kuralı. */
11
+ export declare const GIRIS_KURALI_HOCAYA = "Kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 ve parolan\u0131z\u0131 yaln\u0131z OBS'nin kendi giri\u015F ekran\u0131na, kendiniz yaz\u0131n. Bana, sohbete ya da Akredit'e asla yazmay\u0131n. Do\u011Frulama kodu (SMS, e-posta) ve CAPTCHA da yaln\u0131z sizindir.";
12
+ export declare const MCP_ADRESI = "https://akredit.org/mcp";
13
+ export declare const ISTEMCILER: readonly ["claude_web", "claude_masaustu", "claude_chrome", "chatgpt", "cursor", "vscode", "claude_code"];
14
+ export type Istemci = (typeof ISTEMCILER)[number];
15
+ /** Bağlayıcı ekleme adımları; site (akredit.org ana sayfası) aynı adımları buradan okur. */
16
+ export declare function baglayiciAdimlari(istemci: Istemci): Adim[];
17
+ export declare const ENGELLER: readonly ["tarayici_araci_yok", "site_acilmiyor", "giris_duvari", "captcha", "cerceve", "oturum_dustu", "sayfa_okunamiyor", "kaydet_basilamiyor", "akredit_gorunmuyor"];
18
+ export type Engel = (typeof ENGELLER)[number];
19
+ export declare const kurulumYardimiAraci: import("./temel.ts").AcikArac<z.ZodObject<{
20
+ istemci: z.ZodEnum<{
21
+ claude_web: "claude_web";
22
+ claude_masaustu: "claude_masaustu";
23
+ claude_chrome: "claude_chrome";
24
+ chatgpt: "chatgpt";
25
+ cursor: "cursor";
26
+ vscode: "vscode";
27
+ claude_code: "claude_code";
28
+ }>;
29
+ konu: z.ZodDefault<z.ZodEnum<{
30
+ baglayici: "baglayici";
31
+ hepsi: "hepsi";
32
+ tarayici: "tarayici";
33
+ }>>;
34
+ }, z.core.$strip>>;
35
+ export declare const engelCozAraci: import("./temel.ts").AcikArac<z.ZodObject<{
36
+ engel: z.ZodEnum<{
37
+ cerceve: "cerceve";
38
+ tarayici_araci_yok: "tarayici_araci_yok";
39
+ site_acilmiyor: "site_acilmiyor";
40
+ giris_duvari: "giris_duvari";
41
+ captcha: "captcha";
42
+ oturum_dustu: "oturum_dustu";
43
+ sayfa_okunamiyor: "sayfa_okunamiyor";
44
+ kaydet_basilamiyor: "kaydet_basilamiyor";
45
+ akredit_gorunmuyor: "akredit_gorunmuyor";
46
+ }>;
47
+ istemci: z.ZodOptional<z.ZodEnum<{
48
+ claude_web: "claude_web";
49
+ claude_masaustu: "claude_masaustu";
50
+ claude_chrome: "claude_chrome";
51
+ chatgpt: "chatgpt";
52
+ cursor: "cursor";
53
+ vscode: "vscode";
54
+ claude_code: "claude_code";
55
+ }>>;
56
+ }, z.core.$strip>>;
@@ -0,0 +1,371 @@
1
+ // Kurulum ve engel çözme rehberi. Metinler sabittir, model üretmez. Hocanın kendi OBS'sine tarayıcıyla girmesi
2
+ // için adımlar verir. Değişmez kural: kullanıcı adı, parola ve doğrulama kodu yalnız OBS'nin kendi ekranına,
3
+ // hocanın kendisi tarafından yazılır; asistana ve Akredit'e hiç yazılmaz. CAPTCHA'yı hoca çözer. OBS'de Kaydet'e
4
+ // hoca ya da hocanın açık izniyle asistan basar; onaya Gönder düğmesine hoca basar.
5
+ //
6
+ // Menü ve düğme adları istemcinin sürümüne göre değişir. Doğrulanmamış adlar `degisebilir: true` ile işaretlidir.
7
+ import { z } from "zod";
8
+ import { arac, KOD_KAYNAGI } from "./temel.js";
9
+ const numarala = (xs) => xs.map(([yap, goreceginiz, degisebilir], i) => ({
10
+ no: i + 1,
11
+ yap,
12
+ ...(goreceginiz ? { goreceginiz } : {}),
13
+ ...(degisebilir ? { degisebilir: true } : {}),
14
+ }));
15
+ /** Her rehberin başında hocaya söylenecek giriş kuralı. */
16
+ export const GIRIS_KURALI_HOCAYA = "Kullanıcı adınızı ve parolanızı yalnız OBS'nin kendi giriş ekranına, kendiniz yazın. Bana, sohbete ya da Akredit'e asla yazmayın. Doğrulama kodu (SMS, e-posta) ve CAPTCHA da yalnız sizindir.";
17
+ export const MCP_ADRESI = "https://akredit.org/mcp";
18
+ export const ISTEMCILER = [
19
+ "claude_web",
20
+ "claude_masaustu",
21
+ "claude_chrome",
22
+ "chatgpt",
23
+ "cursor",
24
+ "vscode",
25
+ "claude_code",
26
+ ];
27
+ const ISTEMCI_ADI = {
28
+ claude_web: "Claude (claude.ai, web)",
29
+ claude_masaustu: "Claude masaüstü uygulaması",
30
+ claude_chrome: "Claude in Chrome (Chrome eklentisi)",
31
+ chatgpt: "ChatGPT",
32
+ cursor: "Cursor",
33
+ vscode: "VS Code",
34
+ claude_code: "Claude Code",
35
+ };
36
+ const BAGLAYICI = {
37
+ claude_web: [
38
+ ["claude.ai adresine girin ve oturum açın.", "Sol tarafta sohbet listesi, ortada yazı kutusu."],
39
+ ["Sol alttaki adınıza tıklayın, Settings (Ayarlar) seçin.", "Ayarlar sayfası açılır.", true],
40
+ ["Connectors (Bağlayıcılar) bölümünü açın.", "Bağlı araçların listesi.", true],
41
+ ["Add custom connector (Özel bağlayıcı ekle) düğmesine basın.", "Ad ve adres soran küçük bir pencere.", true],
42
+ [`Ad olarak Akredit, adres olarak ${MCP_ADRESI} yazın ve Add (Ekle) deyin.`, "Listede Akredit görünür."],
43
+ [
44
+ "Yeni sohbet açın. Yazı kutusunun altındaki + düğmesinden Akredit'in açık olduğunu denetleyin.",
45
+ "Akredit'in yanında açık anahtar.",
46
+ true,
47
+ ],
48
+ [
49
+ "Yazı kutusuna akredit yazın ya da + menüsünden Akredit altındaki akredit başlangıcını seçin.",
50
+ "Odak menüsü: 1-5 arası seçenek.",
51
+ ],
52
+ ],
53
+ claude_masaustu: [
54
+ ["Claude masaüstü uygulamasını açın ve oturum açın.", "claude.ai ile aynı görünüm."],
55
+ [
56
+ "Sol alttaki adınızdan Settings (Ayarlar), ardından Connectors (Bağlayıcılar) bölümüne girin.",
57
+ "Bağlayıcı listesi.",
58
+ true,
59
+ ],
60
+ [`Add custom connector ile ad Akredit, adres ${MCP_ADRESI} olarak ekleyin.`, "Listede Akredit görünür.", true],
61
+ ["Yeni sohbette akredit yazın.", "Odak menüsü."],
62
+ ],
63
+ claude_chrome: [
64
+ ["Önce Claude'a Akredit bağlayıcısını ekleyin (Claude web adımları).", "Sohbette Akredit açık."],
65
+ [
66
+ "Chrome tarayıcısını açın. Chrome Web Mağazası'nda Claude eklentisini arayın (Claude in Chrome).",
67
+ "Eklentinin sayfası.",
68
+ true,
69
+ ],
70
+ ["Chrome'a ekle düğmesine basın, izin penceresinde onaylayın.", "Sağ üstte Claude simgesi.", true],
71
+ ["Yapboz simgesinden Claude'u sabitleyin (iğne simgesi).", "Claude simgesi araç çubuğunda kalır.", true],
72
+ ["Simgeye tıklayın, Claude hesabınızla oturum açın.", "Tarayıcının kenarında Claude paneli açılır.", true],
73
+ [
74
+ "OBS sayfanızı aynı pencerede açın; panelde akredit yazın.",
75
+ "Asistan OBS sayfasını okumak için izin ister; siz onaylarsınız.",
76
+ ],
77
+ [
78
+ "Eklenti bazı planlarda ya da kurum hesaplarında kapalı olabilir; o durumda kopyala-yapıştır yedeğini kullanın.",
79
+ undefined,
80
+ true,
81
+ ],
82
+ ],
83
+ chatgpt: [
84
+ ["chatgpt.com adresinde oturum açın.", "Sohbet ekranı."],
85
+ [
86
+ "Settings (Ayarlar), Apps (Uygulamalar), Advanced settings altında Developer mode (geliştirici kipi) seçeneğini açın.",
87
+ "Uygulama oluşturma düğmesi görünür.",
88
+ true,
89
+ ],
90
+ [
91
+ `Create (Oluştur) ile yeni uygulama ekleyin: ad Akredit, adres ${MCP_ADRESI}, kimlik doğrulama yok.`,
92
+ "Akredit araçları listelenir.",
93
+ true,
94
+ ],
95
+ ["İş ve kurum hesaplarında bu ayarı yönetici açar; açamıyorsanız yöneticinize sorun.", undefined, true],
96
+ ["Yeni sohbette Akredit'i seçin ve akredit yazın.", "Odak menüsü."],
97
+ ],
98
+ cursor: [
99
+ ["Cursor'da Settings (Ayarlar), ardından MCP bölümünü açın.", "MCP sunucu listesi.", true],
100
+ [
101
+ `Add new MCP server ile ad akredit, adres ${MCP_ADRESI} girin. Ya da .cursor/mcp.json dosyasına ekleyin.`,
102
+ "Akredit yeşil noktayla görünür.",
103
+ true,
104
+ ],
105
+ ["Sohbet panelinde eğik çizgi (/) yazın ve akredit seçin.", "Odak menüsü."],
106
+ ],
107
+ vscode: [
108
+ ["Komut paletini açın (Ctrl+Shift+P, Mac'te Cmd+Shift+P).", "Üstte arama kutusu."],
109
+ ["MCP: Add Server yazıp seçin, HTTP türünü seçin.", "Adres soran kutu.", true],
110
+ [`Adres olarak ${MCP_ADRESI}, ad olarak akredit yazın.`, "Akredit sunucu listesinde."],
111
+ ["Copilot sohbetinde ajan (Agent) kipini seçin, eğik çizgiyle akredit komutunu çalıştırın.", "Odak menüsü.", true],
112
+ ],
113
+ claude_code: [
114
+ [
115
+ `Uçbirimde şunu çalıştırın: claude mcp add --transport http akredit ${MCP_ADRESI}`,
116
+ "Added HTTP MCP server iletisi.",
117
+ ],
118
+ ["claude komutuyla oturumu açın, /mcp ile Akredit'in bağlı olduğunu denetleyin.", "akredit: connected.", true],
119
+ ["Eğik çizgiyle akredit komutunu seçin.", "Odak menüsü."],
120
+ ],
121
+ };
122
+ /** Bağlayıcı ekleme adımları; site (akredit.org ana sayfası) aynı adımları buradan okur. */
123
+ export function baglayiciAdimlari(istemci) {
124
+ return numarala(BAGLAYICI[istemci]);
125
+ }
126
+ const TARAYICI = {
127
+ claude_web: [
128
+ [
129
+ "claude.ai web sürümü kendi başına OBS'nize giremez. Tarayıcı için Claude in Chrome eklentisini ya da masaüstü uygulamasını kullanın.",
130
+ undefined,
131
+ ],
132
+ [
133
+ "Bunlar yoksa kopyala-yapıştır yedeği: OBS'de ders sayfasını açın, Ctrl+A ile hepsini seçin, Ctrl+C ile kopyalayın, sohbete Ctrl+V ile yapıştırın.",
134
+ "Asistan sayfayı ayrıştırır.",
135
+ ],
136
+ ],
137
+ claude_masaustu: [
138
+ [
139
+ "Masaüstü uygulamasında sohbetin yanında tarayıcı paneli açılabilir. Panelin düğmesi ya da menüsü sürüme göre değişir.",
140
+ "Sohbetin yanında küçük bir tarayıcı.",
141
+ true,
142
+ ],
143
+ ["Asistandan OBS adresinizi açmasını isteyin ya da adresi panele siz yazın.", "OBS giriş ekranı."],
144
+ [
145
+ "Kullanıcı adınızı ve parolanızı panelde OBS'nin kendi kutularına kendiniz yazın. Sohbete yazmayın.",
146
+ "OBS ana sayfası.",
147
+ ],
148
+ [
149
+ "Asistana 'girdim' yazın. Asistan ders listesini ve Bologna sayfasını okur.",
150
+ "Ders listesi sohbette numaralı görünür.",
151
+ ],
152
+ ],
153
+ claude_chrome: [
154
+ ["Chrome'da yeni sekmede OBS adresinizi açın.", "OBS giriş ekranı."],
155
+ [
156
+ "Kullanıcı adınızı ve parolanızı OBS'nin kendi kutularına kendiniz yazın; varsa doğrulama kodunu ve CAPTCHA'yı siz girin.",
157
+ "OBS ana sayfası.",
158
+ ],
159
+ [
160
+ "Claude simgesine tıklayıp paneli açın, 'OBS'ye girdim' yazın.",
161
+ "Asistan sayfayı okumak için izin isteyebilir; onaylayın.",
162
+ true,
163
+ ],
164
+ ["Asistan ders listesini okur, dersinizi numarasıyla seçersiniz.", "Ders bilgi paketi alanları."],
165
+ [
166
+ "Asistan formu doldurursa her alanı gözle denetleyin. Kaydet'e siz basarsınız; isterseniz açık izninizle asistan basar. Onaya Gönder düğmesine siz basarsınız.",
167
+ "OBS'nin kayıt iletisi.",
168
+ ],
169
+ ],
170
+ chatgpt: [
171
+ [
172
+ "Yazı kutusundaki Araçlar (Tools) menüsünden Ajan modunu (Agent mode) seçin.",
173
+ "ChatGPT kendi tarayıcısını açar.",
174
+ true,
175
+ ],
176
+ ["OBS adresinizi açmasını isteyin.", "OBS giriş ekranı ajanın tarayıcısında."],
177
+ [
178
+ "Giriş ekranında Devral (Take over) düğmesine basın; kullanıcı adı ve parolayı kendiniz yazın. Sohbete yazmayın.",
179
+ "OBS ana sayfası.",
180
+ true,
181
+ ],
182
+ ["Denetimi asistana geri verin.", "Asistan sayfayı okur."],
183
+ ["Kaydet'e siz basarsınız ya da açık izninizle asistan basar. Onaya Gönder düğmesine siz basarsınız.", undefined],
184
+ ],
185
+ cursor: [
186
+ ["Cursor'un tarayıcı aracı sürüme göre değişir. Varsa asistandan OBS adresini açmasını isteyin.", undefined, true],
187
+ ["Yoksa kopyala-yapıştır yedeği: sayfada Ctrl+A, Ctrl+C, sohbete Ctrl+V.", "Asistan sayfayı ayrıştırır."],
188
+ ],
189
+ vscode: [
190
+ ["VS Code'da OBS'ye tarayıcıyla girmek için ek araç gerekir; bu en zor yoldur.", undefined, true],
191
+ [
192
+ "En kolayı kopyala-yapıştır: tarayıcınızda OBS sayfasında Ctrl+A, Ctrl+C, sohbete Ctrl+V.",
193
+ "Asistan sayfayı ayrıştırır.",
194
+ ],
195
+ ],
196
+ claude_code: [
197
+ [
198
+ "Claude Code'da tarayıcı için Claude in Chrome eklentisi bağlanabilir; adımlar sürüme göre değişir.",
199
+ undefined,
200
+ true,
201
+ ],
202
+ ["Yoksa kopyala-yapıştır yedeği: sayfada Ctrl+A, Ctrl+C, uçbirime yapıştırın.", "Asistan sayfayı ayrıştırır."],
203
+ ],
204
+ };
205
+ export const ENGELLER = [
206
+ "tarayici_araci_yok",
207
+ "site_acilmiyor",
208
+ "giris_duvari",
209
+ "captcha",
210
+ "cerceve",
211
+ "oturum_dustu",
212
+ "sayfa_okunamiyor",
213
+ "kaydet_basilamiyor",
214
+ "akredit_gorunmuyor",
215
+ ];
216
+ const KOPYALA_YAPISTIR = [
217
+ ["Yedek yol: OBS'de ders bilgi paketi sayfasını kendiniz açın.", "Ders sayfası."],
218
+ ["Sayfanın boş bir yerine tıklayın, Ctrl+A ile hepsini seçin (Mac'te Cmd+A).", "Sayfa mavi seçilir."],
219
+ ["Ctrl+C ile kopyalayın (Mac'te Cmd+C).", undefined],
220
+ [
221
+ "Sohbete dönüp Ctrl+V ile yapıştırın ve gönderin.",
222
+ "Asistan alanları ayrıştırıp bulunan ve bulunamayan alanları söyler.",
223
+ ],
224
+ ];
225
+ const ENGEL_ADIMLARI = {
226
+ tarayici_araci_yok: {
227
+ ad: "Asistanın tarayıcı aracı yok",
228
+ adimlar: [
229
+ [
230
+ "Claude kullanıyorsanız Claude in Chrome eklentisini kurun (kurulum_yardimi, istemci: claude_chrome).",
231
+ undefined,
232
+ ],
233
+ ["Claude masaüstü uygulamasında sohbetin yanındaki tarayıcı panelini açmayı deneyin.", undefined, true],
234
+ ["ChatGPT'de Araçlar menüsünden Ajan modunu seçin.", undefined, true],
235
+ ],
236
+ yedek: true,
237
+ },
238
+ site_acilmiyor: {
239
+ ad: "OBS sitesi açılmıyor",
240
+ adimlar: [
241
+ ["Adresi kendi tarayıcınızda açıp deneyin. Açılıyorsa adresi asistana olduğu gibi verin.", undefined],
242
+ [
243
+ "Kurum dışından erişim kapalı olabilir (VPN ya da kampüs ağı). Kurumunuzun bilgi işlem sayfasına bakın.",
244
+ undefined,
245
+ ],
246
+ ["Site bakımda olabilir; birkaç dakika sonra yeniden deneyin.", undefined],
247
+ ],
248
+ yedek: true,
249
+ },
250
+ giris_duvari: {
251
+ ad: "Giriş ekranı",
252
+ adimlar: [
253
+ ["Bu normaldir. Kullanıcı adınızı ve parolanızı OBS'nin kendi kutularına kendiniz yazın.", "OBS ana sayfası."],
254
+ [
255
+ "Asistana yalnız 'girdim' yazın. Parolayı sohbete yazmayın; yazdıysanız silin ve parolanızı değiştirin.",
256
+ undefined,
257
+ ],
258
+ ["e-Devlet ya da kurum tek oturum açma ekranı gelirse onu da kendiniz tamamlayın.", undefined],
259
+ ],
260
+ yedek: false,
261
+ },
262
+ captcha: {
263
+ ad: "Ben robot değilim (CAPTCHA) kutusu",
264
+ adimlar: [
265
+ ["CAPTCHA'yı siz çözün; asistan çözmez.", undefined],
266
+ ["Çözdükten sonra asistana 'devam' yazın.", "Asistan kaldığı yerden sürer."],
267
+ ],
268
+ yedek: false,
269
+ },
270
+ cerceve: {
271
+ ad: "Sayfa çerçeve (iframe) içinde, asistan içeriği göremiyor",
272
+ adimlar: [
273
+ [
274
+ "Ders sayfasındaki bağlantıya sağ tıklayın, Bağlantıyı yeni sekmede aç seçin.",
275
+ "Sayfa tek başına açılır.",
276
+ true,
277
+ ],
278
+ ["Asistandan bu yeni sekmeyi okumasını isteyin.", undefined],
279
+ ],
280
+ yedek: true,
281
+ },
282
+ oturum_dustu: {
283
+ ad: "Oturum düştü ya da süre doldu",
284
+ adimlar: [
285
+ ["OBS'ye yeniden kendiniz girin (kullanıcı adı ve parolayı yalnız OBS ekranına).", "OBS ana sayfası."],
286
+ [
287
+ "Kaydedilmemiş alanlar silinmiş olabilir. Asistandan son seçilen metni yeniden istemek için 'kopya metnini ver' yazın.",
288
+ undefined,
289
+ ],
290
+ ["Uzun formlarda her sekmeyi doldurduktan sonra Kaydet'e basın.", undefined],
291
+ ],
292
+ yedek: false,
293
+ },
294
+ sayfa_okunamiyor: {
295
+ ad: "Asistan sayfayı okuyamıyor ya da alanları bulamıyor",
296
+ adimlar: [
297
+ ["Sayfanın tamamen yüklendiğini bekleyin ve asistandan yeniden okumasını isteyin.", undefined],
298
+ ["Bologna sayfasında dersin Ders Bilgileri sekmesinde olduğunuzu denetleyin.", undefined, true],
299
+ ],
300
+ yedek: true,
301
+ },
302
+ kaydet_basilamiyor: {
303
+ ad: "Kaydet ya da Gönder",
304
+ adimlar: [
305
+ [
306
+ "Kaydet'e siz basarsınız; isterseniz asistan sizin açık izninizle (evet ya da bir kez hepsini kaydet) basar. Onaya Gönder düğmesine yalnız siz basarsınız.",
307
+ undefined,
308
+ ],
309
+ ["Basmadan önce alanları gözle denetleyin.", undefined],
310
+ ["Düğme pasifse zorunlu bir alan boş olabilir; OBS'nin kırmızı uyarısına bakın ve asistana söyleyin.", undefined],
311
+ ],
312
+ yedek: false,
313
+ },
314
+ akredit_gorunmuyor: {
315
+ ad: "Akredit araçları ya da akredit komutu görünmüyor",
316
+ adimlar: [
317
+ ["Yeni bir sohbet açın; bağlayıcı eski sohbette görünmeyebilir.", undefined],
318
+ ["Yazı kutusunun altındaki + ya da araç menüsünden Akredit'in açık olduğunu denetleyin.", undefined, true],
319
+ ["Komut görünmüyorsa 'Akredit ile başlayalım' yazmanız yeter.", "Asistan odak menüsünü gösterir."],
320
+ [`Bağlayıcı adresinin tam olarak ${MCP_ADRESI} olduğunu denetleyin.`, undefined],
321
+ ],
322
+ yedek: false,
323
+ },
324
+ };
325
+ export const kurulumYardimiAraci = arac({
326
+ ad: "kurulum_yardimi",
327
+ baslik: "Kurulum yardımı",
328
+ aciklama: "Akredit bağlayıcısını eklemek ve OBS'ye tarayıcıyla girmek için istemciye göre adım adım rehber verir: Claude web ve masaüstü, Claude in Chrome, ChatGPT, Cursor, VS Code, Claude Code. Her adımda ekranda ne görüleceğini söyler.",
329
+ sonrakiAdim: "Adımları hocaya tek tek gösterin: bir adım verin, 'oldu' demesini bekleyin, sonra sıradakine geçin. 'değişebilir' işaretli menü adını 'sizde biraz farklı olabilir' diye söyleyin. Giriş kuralını hocaya bir kez açıkça söyleyin.",
330
+ girdi: z.object({
331
+ istemci: z.enum(ISTEMCILER).describe("Hocanın kullandığı asistan"),
332
+ konu: z
333
+ .enum(["baglayici", "tarayici", "hepsi"])
334
+ .default("hepsi")
335
+ .describe("Bağlayıcı ekleme, OBS'ye tarayıcıyla giriş ya da ikisi"),
336
+ }),
337
+ calistir: ({ istemci, konu }) => ({
338
+ sonuc: {
339
+ istemci: ISTEMCI_ADI[istemci],
340
+ girisKurali: GIRIS_KURALI_HOCAYA,
341
+ ...(konu !== "tarayici" ? { baglayiciAdimlari: numarala(BAGLAYICI[istemci]) } : {}),
342
+ ...(konu !== "baglayici" ? { tarayiciAdimlari: numarala(TARAYICI[istemci]) } : {}),
343
+ not: "Menü adları istemcinin sürümüne ve diline göre değişebilir. 'degisebilir' işaretli adlar doğrulanmadı.",
344
+ },
345
+ kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/yardim.ts", "Akredit kurulum rehberi")],
346
+ }),
347
+ });
348
+ export const engelCozAraci = arac({
349
+ ad: "engel_coz",
350
+ baslik: "Engel çöz",
351
+ aciklama: "OBS'ye girerken ya da sayfayı okurken çıkan engel için numaralı çözüm adımları verir: tarayıcı aracı yok, site açılmıyor, giriş ekranı, CAPTCHA, çerçeve, oturum düşmesi, sayfa okunamıyor, Kaydet düğmesi, Akredit görünmüyor. Gerekirse kopyala-yapıştır yedeğini de verir.",
352
+ sonrakiAdim: "Adımları hocaya tek tek gösterin. Hiçbiri olmazsa kopyala-yapıştır yedeğine geçin. Parola, doğrulama kodu ya da çerez istemeyin.",
353
+ girdi: z.object({
354
+ engel: z.enum(ENGELLER),
355
+ istemci: z.enum(ISTEMCILER).optional(),
356
+ }),
357
+ calistir: ({ engel, istemci }) => {
358
+ const e = ENGEL_ADIMLARI[engel];
359
+ const ozel = engel === "tarayici_araci_yok" && istemci ? TARAYICI[istemci] : [];
360
+ return {
361
+ sonuc: {
362
+ engel: e.ad,
363
+ girisKurali: GIRIS_KURALI_HOCAYA,
364
+ adimlar: numarala([...ozel, ...e.adimlar]),
365
+ ...(e.yedek ? { yedekYol: numarala(KOPYALA_YAPISTIR) } : {}),
366
+ obsyeYazmaYedegi: "Tarayıcı aracı yoksa asistan bologna_kopya_metni ile alan alan kopyalanacak metni verir; siz OBS'de ilgili kutuya yapıştırıp Kaydet'e basarsınız.",
367
+ },
368
+ kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/yardim.ts", "Akredit engel çözme rehberi")],
369
+ };
370
+ },
371
+ });