---
title: 'Sunucu Yazımı'
source: 'https://academia.sh/tr/kurslar/mcp/sunucu-yazimi'
course: 'Model Bağlam Protokolü ve Araç Ekosistemi'
language: tr
updated: '2026-08-17T18:06:40+00:00'
license: 'CC BY-SA 4.0'
---

# Sunucu Yazımı

Bir sunucunun işleyicileri ile yetenek bildiriminin ayrılması ve bildirimin eksiksizliğinin çağrılabilirlik olarak ölçülmesi: aynı üç kurgu sunucu ve aynı on iki görev dört bildirim düzeyinde koşturulduğunda el sıkışmasının bedeli dört düzeyde de 3 istek, 3 yanıt, 99 belirteç ve 5 gidiş-dönüştür, değişen tek şey o üç yanıtın istemcinin eline ne bıraktığıdır. Yalnız ad bildiren düzeyde anlaşılan araç dörttür ama istemci alan dolduramadığı için 12 çağrının 12'si başarısız olur ve tamamlanan görev sıfırdır. Alan bildirilince çağrı 30'a, tamamlanan görev 9'a çıkar. Yan etkinin bildirilmesi bu izin listesinde hiçbir sayıyı oynatmaz. Sürümün bildirilmesi anlaşılan aracı 4'ten 3'e düşürür, tamamlanan görevi 6'ya indirir ve zarf belirtecini 816'dan 744'e çeker: en eksiksiz bildirim en küçük araç kümesini verir. İzin listesi kayit_yaz'ı kabul edecek biçimde genişletildiğinde yan etkisi bildirilmeyen üç yazma çağrısı sessizce geçer, bildirildiğinde aynı üç çağrı onay kapısında durdurulur.

Önceki beş derste model bağlam protokolü dışarıdan anlatıldı: üç rolün sorumluluğu ayrıldı,
aktarım katmanının gidiş-dönüşü sayıldı, veri katmanının mesaj türleri ölçüldü ve bir sunucunun
bildirdiği beş araçtan yalnız üçünün anlaşıldığı görüldü. Bütün o ölçümlerde sunucu **verilmiş**
bir şeydi. Hiçbiri yazılmadı.

Bu ders sunucuyu yazar. Yazılan şey iki parçadır ve aradaki ayrım kursun kalan ölçümlerini taşır.
**İşleyici**, gelen isteği karşılayıp sonucu döndüren işlevdir; sunucunun içindedir ve istemci onu
hiç görmez. **Yetenek bildirimi**, o işleyicinin dışarıya verilen tanımıdır ve istemcinin gördüğü
tek şeydir. Araç tanımının kattığı Yapay Zekâ Ajanları kursunda ölçülmüştü: yalnız adla verilen
tanımda geçerli çağrı oranı 0,2803, şemayla 0,8919. O ölçüm tanımın **içeriğini** sorguluyordu ve
tanım elin altındaydı. Burada tanım karşı taraftadır ve sorulan şey **eksiksizliğidir**: işleyici
yerinde durduğu hâlde, bildirilmeyen bir alan istemcinin elinde hiç yoktur.

- **GE1.** Ortam, üç sunucu ve ders içindeki protokol benzeticisi **kurgudur**; gerçek bir sunucu,
  istemci kitaplığı ya da çalışma zamanı çağrılmaz. 60 kurgu abone, üç kurgu sunucu, beş
  bildirilen araç. Tohum **20260218**, ikinci koşum **20260219**.
- **GE2.** Dört düzeyin hepsinde işleyiciler **aynıdır**, değişen yalnız bildirimdir: yalnız ad;
  ad ve alan; ad, alan ve yan etki; tam bildirim (sürüm eklenmiş).
- **GE3.** İstemci yalnız bildirileni kullanır. Alan bildirilmemişse çağrı **alansız** gider.
- **GE4.** Zarf her istek ve yanıtı sayar; sabit bedeli 8 belirteçtir ve uzak aktarımda gidiş-dönüş
  iki katıdır.
- **GE5.** İzin listesi **ada** bakar ve dört araç taşır; `kayit_yaz` listede yoktur. İstemci
  `kayit` sunucusunun sürüm 3'ünü değil, sürüm 2'sini bilir.
- **GE6.** Görev kümesi 12 kurgu görevdir: dört türden üçer tane, her biri bilinen bir araç
  zinciriyle çözülür. Bir çağrı hata döndürürse zincir orada kesilir.
- **GE7.** Tamamlanan görev, zincirin bıraktığı sonucun doğru yanıta eşit olmasıdır.
- **GE8.** Yan etkili bir araç, yan etkisi **bildirilmişse** onay ister; bildirilmemişse istemci
  onay kapısını kuramaz.
- **GE9.** Taban çizgisi protokolsüz kurulumdur: aracın ders içinde tanımlandığı, el sıkışmasının
  olmadığı, zarfın sayılmadığı hâl.
- **GE10.** Küme küçüktür: üç sunucu, beş bildirilen araç. Bu yüzden oran değil **sayım** basılır;
  bu kümenin ölçebildiği en küçük adım 1/5, yani 0,2000'dir.

## İşleyici ve Bildirim

Aşağıdaki kod kurgu ortamı, üç kurgu sunucuyu ve zarf sayımını kurar, sonra bildirimi işleyiciden
ayıran iki işlev ekler: `bildir` sunucunun dışarıya verdiği tanımı bir düzeyde üretir, `uzlas` o
tanımla yetenek uzlaşmasını yapar.

```python
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


def belirtecle(m):
    b = []
    for s in str(m).lower().replace(",", " , ").split():
        while len(s) > 5:
            b.append(s[:5])
            s = s[5:]
        b.append(s)
    return b


# ---- KURGU ortam ve araclar (K05 ile ayni kurgu, burada sunucuya tasiniyor)
BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
ABONE = {}
for i in range(60):
    r = uretec(TOHUM + 31 * i)
    onceki = 10000 + int(r() * 80000)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "onceki": onceki, "simdiki": onceki + int(r() * 70)}
BASAMAK, USTU, ISYERI_KAT = ((10, 4), (25, 7), (40, 11)), 16, 2


def _bedel(m3, tip):
    kalan, alt, top = m3, 0, 0
    for ust, birim in BASAMAK:
        dilim = min(kalan, ust - alt)
        if dilim <= 0:
            break
        top += dilim * birim
        kalan -= dilim
        alt = ust
    return (top + kalan * USTU) * (ISYERI_KAT if tip == "isyeri" else 1)


def t_sayac(abone=None, **_):
    a = ABONE.get(abone)
    return {"hata": "abone yok"} if a is None else {
        "onceki": a["onceki"], "simdiki": a["simdiki"], "tip": a["tip"]}


def t_tuketim(onceki=None, simdiki=None, **_):
    if onceki is None or simdiki is None:
        return {"hata": "eksik alan"}
    f = simdiki - onceki
    return {"m3": f + 100000 if f < 0 else f}


def t_tarife(m3=None, tip="mesken", **_):
    return {"hata": "eksik alan"} if m3 is None else {"tutar": _bedel(m3, tip)}


def t_bolge(bolge=None, **_):
    if bolge not in BOLGE:
        return {"hata": "bolge yok"}
    n = sum(1 for a in ABONE.values() if a["bolge"] == bolge)
    return {"bolge": bolge, "abone": n}


def t_kayit(abone=None, sutun=None, deger=None, **_):
    if not (abone and sutun and deger is not None):
        return {"hata": "eksik alan"}
    return {"yazildi": True}


# ---- KURGU sunucular: her biri isleyici tutar ve yetenek bildirir
SUNUCU = {
    "olcum": {"surum": 2, "aktarim": "yerel",
              "arac": {"sayac_oku": {"islev": t_sayac, "alan": ("abone",), "yan_etki": False},
                       "tuketim": {"islev": t_tuketim, "alan": ("onceki", "simdiki"),
                                   "yan_etki": False}},
              "kaynak": ("abone_listesi",), "istem": ()},
    "tarife": {"surum": 1, "aktarim": "uzak",
               "arac": {"tarife": {"islev": t_tarife, "alan": ("m3", "tip"), "yan_etki": False}},
               "kaynak": ("basamak_tablosu",), "istem": ("bedel_aciklamasi",)},
    "kayit": {"surum": 3, "aktarim": "uzak",
              "arac": {"bolge_ozet": {"islev": t_bolge, "alan": ("bolge",), "yan_etki": False},
                       "kayit_yaz": {"islev": t_kayit, "alan": ("abone", "sutun", "deger"),
                                     "yan_etki": True}},
              "kaynak": (), "istem": ()},
}
ISTEMCI_SURUM = {"olcum": 2, "tarife": 1, "kayit": 2}      # kayit sunucusu ileride
IZIN = ("sayac_oku", "tuketim", "tarife", "bolge_ozet")     # kayit_yaz izin listesinde yok

SAYIM = {"istek": 0, "yanit": 0, "bildirim": 0, "belirtec": 0, "gidis_donus": 0}


def mesaj(tur, govde, aktarim="yerel"):
    """KURGU zarf. Gercek bir protokol uygulanmaz; sayilan sey zarfin bedelidir."""
    SAYIM[tur] += 1
    b = len(belirtecle(govde)) + 8                       # zarf sabiti
    SAYIM["belirtec"] += b
    if tur == "istek":
        SAYIM["gidis_donus"] += 2 if aktarim == "uzak" else 1
    return b


# ---- Bildirim duzeyleri: sunucunun disariya verdigi tanim ne tasiyor
DUZEY = ("ad", "ad + alan", "ad + alan + yan etki", "tam")


def bildir(s, d):
    """Sunucunun bildirimi; duzey, isleyicinin hangi yuzunun disari ciktigini belirler."""
    b = {"arac": {}}
    for ad, t in s["arac"].items():
        tan = {}
        if d >= 1:
            tan["alan"] = t["alan"]
        if d >= 2:
            tan["yan_etki"] = t["yan_etki"]
        b["arac"][ad] = tan
    if d >= 3:
        b["surum"] = s["surum"]
    return b


def uzlas(d, izin=IZIN):
    """Yetenek uzlasmasi yalniz bildirilenle yapilir: bildirilmeyen uzlasilamaz."""
    anlasilan, dusen = {}, []
    for ad, s in SUNUCU.items():
        b = bildir(s, d)
        mesaj("istek", f"yetenek sor {ad}", s["aktarim"])
        mesaj("yanit", f"yetenek bildir {ad} surum {s['surum']} "
                       + " ".join(s["arac"]) + " " + " ".join(s["kaynak"]))
        if "surum" in b and ISTEMCI_SURUM.get(ad, 0) < b["surum"]:
            dusen += [(t, "surum") for t in b["arac"]]
            continue
        for t, tan in b["arac"].items():
            if t not in izin:
                dusen.append((t, "izin"))
                continue
            anlasilan[t] = dict(tan, islev=s["arac"][t]["islev"], aktarim=s["aktarim"])
    return anlasilan, dusen


def cagir(anlasilan, ad, durum, onay=False):
    """Istemci yalniz bildirilen alani doldurur; bildirilen yan etki onay ister."""
    if ad not in anlasilan:
        return {"hata": "arac bildirilmedi"}
    t = anlasilan[ad]
    if t.get("yan_etki") and not onay:
        return {"hata": "onay yok"}
    kw = {a: durum[a] for a in t.get("alan", ()) if a in durum}
    mesaj("istek", f"{ad} " + " ".join(f"{k} {v}" for k, v in kw.items()), t["aktarim"])
    s = t["islev"](**kw)
    mesaj("yanit", " ".join(f"{k} {v}" for k, v in s.items()))
    return s


print("duzey                  olcum sunucusunun bildirimi ( sayac_oku )")
for d, ad in enumerate(DUZEY):
    b = bildir(SUNUCU["olcum"], d)
    print(f"{d} {ad:<20} surum {b.get('surum', '-')} , {b['arac']['sayac_oku']}")
print()
print("duzey                  anlasilan  dusen  istek  yanit  belirtec  gidis-donus")
for d, ad in enumerate(DUZEY):
    for k in SAYIM:
        SAYIM[k] = 0
    a, ds = uzlas(d)
    print(f"{d} {ad:<20} {len(a):>9} {len(ds):>6} {SAYIM['istek']:>6} {SAYIM['yanit']:>6}"
          f" {SAYIM['belirtec']:>9} {SAYIM['gidis_donus']:>12}")
print("tam duzeyde anlasilan :", sorted(uzlas(3)[0]))
```

```
duzey                  olcum sunucusunun bildirimi ( sayac_oku )
0 ad                   surum - , {}
1 ad + alan            surum - , {'alan': ('abone',)}
2 ad + alan + yan etki surum - , {'alan': ('abone',), 'yan_etki': False}
3 tam                  surum 2 , {'alan': ('abone',), 'yan_etki': False}

duzey                  anlasilan  dusen  istek  yanit  belirtec  gidis-donus
0 ad                           4      1      3      3        99            5
1 ad + alan                    4      1      3      3        99            5
2 ad + alan + yan etki         4      1      3      3        99            5
3 tam                          3      2      3      3        99            5
tam duzeyde anlasilan : ['sayac_oku', 'tarife', 'tuketim']
```

İkinci tablonun dört satırında el sıkışmasının bedeli değişmiyor: her düzeyde **3 istek, 3 yanıt,
99 belirteç, 5 gidiş-dönüş**. Eksiksiz bildirim keşif anında bedava geldi; değişen tek şey o üç
yanıtın istemcinin eline ne bıraktığı oldu. İlk üç düzeyde anlaşılan araç **dört**, düşen **bir**
— `kayit_yaz`, adı izin listesinde olmadığı için. Dördüncü düzeyde sürüm bildirildiği anda istemci
bir karşılaştırma yapabiliyor: kendi bildiği sürüm 2, sunucunun bildirdiği 3. `kayit` sunucusunun
tamamı düşüyor ve **`bolge_ozet` de onunla gidiyor**, izin listesinde olduğu hâlde. Anlaşılan araç
dörtten **üçe** iniyor. Kursun ikinci iddiasının bu dersteki karşılığı budur: **en eksiksiz
bildirim en küçük araç kümesini verdi.** Eksik bildiren sunucu daha çok araç anlaştırıyor, çünkü
karşılaştırılacak bir şey söylememiş oluyor.

## Dört Düzeyin Sayısı

Aynı on iki görev dört düzeyde koşturulur. Görev kümesi dört türden üçer tanedir: bedel, tüketim,
bölge ve kayıt. Tek değişken bildirim düzeyidir.

```python
ZINCIR = {"bedel": ("sayac_oku", "tuketim", "tarife"), "tuketim": ("sayac_oku", "tuketim"),
          "bolge": ("bolge_ozet",), "kayit": ("sayac_oku", "tuketim", "tarife", "kayit_yaz")}


def gorevler(tohum=TOHUM):
    """12 KURGU gorev: dort turden ucer tane, abonesi ve bolgesi tohumla secilir."""
    g = []
    for i in range(12):
        r = uretec(tohum + 9973 * i)
        g.append({"tur": list(ZINCIR)[i % 4], "abone": f"A{1 + int(r() * 60):03d}",
                  "bolge": BOLGE[int(r() * 5)]})
    return g


def dogru(g):
    a = ABONE[g["abone"]]
    f = a["simdiki"] - a["onceki"]
    m3 = f + 100000 if f < 0 else f
    if g["tur"] == "tuketim":
        return m3
    if g["tur"] == "bolge":
        return sum(1 for x in ABONE.values() if x["bolge"] == g["bolge"])
    return True if g["tur"] == "kayit" else _bedel(m3, a["tip"])


def kosum(d, izin=IZIN, onay=False, tohum=TOHUM):
    """Ayni 12 gorev, tek degisken bildirim duzeyi."""
    for k in SAYIM:
        SAYIM[k] = 0
    anlasilan = uzlas(d, izin)[0]
    s = {"cagri": 0, "basarisiz": 0, "tamam": 0, "durdurulan": 0, "yazilan": 0}
    for g in gorevler(tohum):
        durum = {"abone": g["abone"], "bolge": g["bolge"], "sutun": "bedel"}
        sonuc = None
        for ad in ZINCIR[g["tur"]]:
            durum["deger"] = sonuc
            y = cagir(anlasilan, ad, durum, onay)
            s["cagri"] += 1
            if "hata" in y:
                s["basarisiz"] += 1
                s["durdurulan"] += y["hata"] == "onay yok"
                break
            durum.update(y)
            s["yazilan"] += y.get("yazildi") is True
            sonuc = y.get("yazildi", y.get("tutar", y.get("m3", y.get("abone", sonuc))))
        s["tamam"] += sonuc == dogru(g)
    return dict(anlasilan=len(anlasilan), **s, **SAYIM)


print("duzey                  anlasilan  cagri  basarisiz  tamamlanan  istek  yanit  belirtec")
for d, ad in enumerate(DUZEY):
    a = kosum(d)
    print(f"{d} {ad:<20} {a['anlasilan']:>9} {a['cagri']:>6} {a['basarisiz']:>10}"
          f" {a['tamam']:>11} {a['istek']:>6} {a['yanit']:>6} {a['belirtec']:>9}")
b = [kosum(d, tohum=TOHUM + 1) for d in range(4)]
print("ikinci kosum ( gorev tohumu 20260219 ) tamamlanan :", [x["tamam"] for x in b],
      ", belirtec :", [x["belirtec"] for x in b])
print()
GENIS = IZIN + ("kayit_yaz",)
print("genis izin listesi ( kayit_yaz eklendi )   anlasilan  durdurulan  yazilan  tamamlanan")
for d, onay in ((1, False), (2, False), (2, True)):
    a = kosum(d, izin=GENIS, onay=onay)
    print(f"duzey {d} , onay {str(onay).lower():<5}                      {a['anlasilan']:>9}"
          f" {a['durdurulan']:>11} {a['yazilan']:>8} {a['tamam']:>11}")
```

```
duzey                  anlasilan  cagri  basarisiz  tamamlanan  istek  yanit  belirtec
0 ad                           4     12         12           0     15     15       351
1 ad + alan                    4     30          3           9     30     30       816
2 ad + alan + yan etki         4     30          3           9     30     30       816
3 tam                          3     30          6           6     27     27       744
ikinci kosum ( gorev tohumu 20260219 ) tamamlanan : [0, 9, 9, 6] , belirtec : [351, 818, 818, 744]

genis izin listesi ( kayit_yaz eklendi )   anlasilan  durdurulan  yazilan  tamamlanan
duzey 1 , onay false                              5           0        3          12
duzey 2 , onay false                              5           3        0           9
duzey 2 , onay true                               5           0        3          12
```

İlk satırda anlaşılan araç dörttür, yani istemci hangi araçların var olduğunu **biliyor**;
işleyiciler de yerinde duruyor. Buna rağmen tamamlanan görev **sıfır**. İstemci hangi alanı
dolduracağını bilmediği için her çağrı alansız gidiyor, `sayac_oku` `abone yok` döndürüyor ve
zincir ilk adımda kesiliyor. Zincirlerin toplam uzunluğu otuz adımken yalnız **12 çağrı**
yapılabiliyor, hepsi başarısız oluyor ve koşum **351 belirteç** harcıyor. En ucuz koşum, hiçbir
işi bitirmeyen koşumdur.

İkinci satırda alan bildiriliyor ve zincirler işliyor: çağrı 30'a, tamamlanan görev **dokuza**
çıkıyor. Kalan üç başarısız çağrı kayıt görevlerinin son adımıdır; `kayit_yaz` izin listesinde
olmadığı için `arac bildirilmedi` dönüyor ve bu üç çağrı **hiç mesaj harcamıyor** — istek 30, yani
3 el sıkışması artı 27 gönderilen çağrı. Anlaşılmamış bir araca yapılan çağrı istemcinin içinde
biter, tele hiç çıkmaz.

Üçüncü satır ikinciyle **birebir aynıdır**. Yan etkinin bildirilmesi bu izin listesinde tek bir
sayıyı bile oynatmadı, çünkü yan etkili tek araç zaten listede değildi. Sınanmayan bir bildirimin
ölçülebilir değeri yoktur. Dördüncü satır ise daha ucuz ve daha kötüdür: zarf belirteci 816'dan
**744'e** iniyor, tamamlanan görev dokuzdan **altıya**. Üç bölge görevi, `bolge_ozet` düştüğü için
tek adımda `arac bildirilmedi` alıyor.

İkinci koşumda görev kümesi başka bir tohumla çekiliyor, yani başka aboneler ve başka bölgeler
geliyor; tamamlanan görev dört düzeyde de aynı kalıyor. Zarf belirteci yalnız iki düzeyde 816'dan
818'e oynuyor, çünkü bir gövdeye giren bölge adı bir belirteç uzun. Sayılan şey verinin değil
**sözleşmenin** özelliğidir.

## Bildirilmeyen Yan Etki

Son üç satır, üçüncü düzeyin niçin boşuna göründüğünü açıklar. İzin listesine `kayit_yaz`
eklendiğinde anlaşılan araç beşe çıkıyor. Yan etki bildirilmemişken (birinci düzey) üç yazma
çağrısı geçiyor ve on iki görevin hepsi tamamlanıyor; istemcinin elinde bu üç çağrının
ötekilerden farklı olduğunu söyleyen **hiçbir alan yoktur**.

Yan etki bildirildiğinde (ikinci düzey) aynı üç çağrı onay kapısında duruyor: **durdurulan 3**,
yazılan 0, tamamlanan 9. Onay verildiğinde yine üçü yazılıyor. Onayın verilip verilmeyeceği
istemcinin izin modelidir ve Yapay Zekâ Ajanları kursunda ölçülmüştür; burada ölçülen şey onun
**önkoşuludur**: bildirilmeyen bir yan etki durdurulamaz, çünkü sorulacak bir soru yoktur. Üç
durdurulan çağrı on iki görevin içinde sayılır; bu küme oran basmak için fazla küçüktür.

## Özet

- Sunucunun iki yüzü ayrılır: işleyici içeride durup hiç görünmez, yetenek bildirimi istemcinin
  elindeki tek şeydir.
- El sıkışmasının bedeli dört bildirim düzeyinde de aynıdır — 3 istek, 3 yanıt, 99 belirteç,
  5 gidiş-dönüş; eksiksizlik keşif anında ücretsizdir.
- Yalnız ad bildiren düzeyde anlaşılan araç dörttür ama tamamlanan görev sıfırdır: 12 çağrının
  12'si alansız gittiği için başarısız olur ve koşum 351 belirteçle en ucuz koşumdur.
- Alan bildirilince çağrı 30'a ve tamamlanan görev 9'a çıkar; anlaşılmamış bir araca yapılan üç
  çağrı hiç mesaj harcamaz.
- Sürüm bildirilince anlaşılan araç 4'ten 3'e, tamamlanan görev 9'dan 6'ya iner: en eksiksiz
  bildirim en küçük araç kümesini verir.
- Yan etki bildirilmemişken üç yazma çağrısı sessizce geçer; bildirildiğinde durdurulur.

## Sonraki Adım

Bu derste sunucu yazıldı ve bildiriminin eksikliği çağrılabilirlik olarak sayıldı. Karşı taraf
hazır kabul edildi: el sıkışmasının çoktan yapılmış olduğu, anlaşılan kümenin bir yerde durduğu ve
kimin ne zaman sorduğu hiç sorulmadı. Sonraki ders istemciyi yazar. Sunucu keşfinin ne tuttuğunu,
aynı keşfin her görevde yenilenmesiyle bir oturumda bir kez ödenmesi arasındaki farkı ve `kayit`
sunucusunun düşmesinin **hiçbir hata üretmediğini** ölçer: keşif mesajları düşen sunucuyla
düşmeyeni birbirinden ayırt etmez, aradaki farkı görünür kılan tek şey sayımdır.
