CodeGym /Kurslar /ChatGPT Apps /MCP-də hadisə modeli: bildiriş tipləri, mesaj formatı, id...

MCP-də hadisə modeli: bildiriş tipləri, mesaj formatı, idempotentlik

ChatGPT Apps
Səviyyə , Dərs
Mövcuddur

1. MCP hadisələri nəyə görə ümumiyyətlə lazımdır

İndiyədək ChatGPT ilə sizin backend arasında ünsiyyətin demək olar hamısı RPC kimi görünürdü: model aləti çağırır, o nəsə edir, nəticəni qaytarır — vəssalam. Bu, əməliyyatlar qısa olanda rahatdır: 200–500 ms, maksimum bir-iki saniyə.

Amma uzunmüddətli bir şey peyda olan kimi — GiftGenius üçün əməkdaşların üstünlükləri olan böyük faylın analizi, çoxsaylı xarici API-lərdən tövsiyələrin yığılması, böyük feed-in yenidən hesablanması — hər şey narahat edir. HTTP‑taymautlar, funksiyaların təkrar işə salınması, “əbədi” spinnerlər, istifadəçi isə oturub düşünür: “bu hələ diridir, yoxsa artıq ölüb?”.

Elə burada hadisə modeli başlayır. Uzun alət çağırışını saxlamaq əvəzinə siz tapşırığı işə salırsınız, jobId alırsınız, sonra isə server öz təşəbbüsü ilə hadisələr göndərir: başladı, irəliləyiş var, hazırdır, yıxıldı. Bu hadisələr MCP-də JSON-RPC notifications kimi reallaşdırılıb — id olmayan, cavab gözlənilməyən birtərəfli mesajlar.

Vacib məqam: hadisə “tel üzərində console.log” deyil. Bu, müəyyən sxemi olan rəsmi protokol mesajıdır və sizin UI (vidcet) və/və ya agent onu alət çağırışının nəticəsi qədər intizamla emal etməyi bacarmalıdır.

Xatırlatma: MCP-də mesaj tipləri

Davam etməzdən əvvəl qısaca xatırlayaq: MCP-də ümumiyyətlə hansı mesajlar olur.

Marketinq qatlarını kənara qoysaq, MCP JSON-RPC 2.0-a söykənir. Orada üç baza mesaj tipi var: sorğular, cavablar və bildirişlər.

Onları siyahı ilə yazmaq əvəzinə kiçik müqayisəli cədvələ baxaq:

Növ id sahəsi İnisiator kimdir Cavab gözlənilirmi? MCP-də nümunə
Request var Adətən müştəri (ChatGPT) Bəli tools/call alət çağırışı
Response var MCP serveri Elə cavabın özüdür tools/call nəticəsi
Notification yox Müştəri və ya server Xeyr notifications/progress, resources/updated, logging/message

MCP hadisələri məhz üçüncü sətirdə yaşayır: bunlar notifications-dır. Fərqləndirici əlamətlər:

  • yuxarı səviyyədə id yoxdur — cavabda heç bir result və ya error gəlməyəcək;
  • inisiator ACK gözləmir — protokol səviyyəsində “atdım və unutdum”;
  • etibarlılıq təsdiqlərə görə yox, emalçıların idempotentliyinə və təkrar göndərmə siyasətinə görə qurulur.

Vacib məhdudiyyət: MCP hadisələri “istənilən an haradasa kosmosda” uçmur. Onlar konkret nəqliyyat üzərində qurulmuş MCP bağlantısı daxilində yaşayır. Ən çox bu, SSE-yə bənzər bir axındır (nəqliyyat detalları və variantları ayrıca mühazirədə müzakirə olunacaq).

2. “MCP hadisəsi” praktikada nədir

Formal olaraq MCP hadisəsi JSON-RPC notification-dır, yəni belə obyekt:

{
  "jsonrpc": "2.0",
  "method": "notifications/job/progress",
  "params": {
    "jobId": "job_123",
    "percentage": 30,
    "stage": "Kataloqda variantları axtarırıq",
    "eventId": "evt_abc123",
    "timestamp": "2025-11-21T10:15:00Z"
  }
}

Burada bir neçə vacib məqam var:

  1. method sahəsində hadisənin tipini və onun “ad sahəsini” kodlaşdırırıq. MCP artıq notifications/... tipli loglar, proqres və resurs dəyişiklikləri üçün bir sıra standart metodları müəyyənləşdirir, amma siz öz biznesəxas metodlarınızı da əlavə edə bilərsiniz və etməlisiniz, məsələn, notifications/job/progress və ya notifications/job/completed.
  2. Bütün biznes məlumatları params daxilindədir. Orada tapşırıq identifikatorlarını (jobId), hadisələrin unikal id-lərini (eventId), zamanı (timestamp), insanın oxuya biləcəyi mesajları və s. saxlayacağıq.
  3. Yuxarı səviyyədə id sahəsi yoxdur — məhz buna görə bu, notification-dır. Ona cavab protokolla nəzərdə tutulmur. Server “anladılarmı” bilmək istəsə, başqa hadisə göndərə bilər və ya müştərinin reaktiv hərəkətlərini (məsələn, yeni sorğu) gözləyə bilər. Amma JSON-RPC baxımından ACK yoxdur.

Zehni model səviyyəsində belə düşünmək olar: tools/call alət çağırışı “cavabını gözlədiyiniz məktub” kimidir, hadisə isə Slack-botdan “bildiriş” kimidir: “Fon tapşırığı #123 tamamlandı”.

3. Hadisələrin taksonomiyası: hansı bildirişlər olur

Sadəcə “istənilən JSON-u notifications kimi göndərin” desəniz, iki həftə sonra sistem zibilxanaya çevrilir: hadisələrin adları fərqlidir, sahələr dəyişir, UI bununla nə edəcəyini anlamır. Buna görə kiçik bir taksonomiya barədə razılaşmaq faydalıdır.

Aşağıda MCP speki və real ChatGPT Apps halları ilə yaxşı uzlaşan rahat bir təsnifat variantı var.

Tapşırığın həyat dövrü hadisələri (Job Lifecycle)

Bunlar tapşırığın vəziyyətinin əsas keçidlərini əks etdirən hadisələrdir. Adətən tapşırığın pendingrunning → (completed | failed | canceled) kimi state machine-i olur.

Tipik hadisələr:

  • job.created — tapşırıq qeydiyyata alındı;
  • job.started — worker işi yerinə yetirməyə başladı;
  • job.completed — tapşırıq uğurla bitdi;
  • job.failed — tapşırıq xəta ilə yıxıldı;
  • job.canceled — tapşırıq istifadəçi tərəfindən ləğv olundu.

GiftGenius üçün job.completed nümunəsi:

{
  "jsonrpc": "2.0",
  "method": "notifications/job/completed",
  "params": {
    "eventId": "evt_gg_100",
    "jobId": "giftjob_42",
    "timestamp": "2025-11-21T10:20:00Z",
    "summary": "Hədiyyələrin seçimi başa çatdı",
    "resultResourceId": "resource:gifts:giftjob_42"
  }
}

Burada resultResourceId sonradan vidcet və ya agentin oxuyacağı MCP resursuna işarə edə bilər.

İrəliləyiş hadisələri (Progress Updates)

Bunlar həyat dövrü daxilində “kiçik addımlar”dır: onlar son statusu dəyişmir, amma istifadəçiyə nəsə baş verdiyini hiss etdirir.

Tipik job.progress hadisəsi:

{
  "jsonrpc": "2.0",
  "method": "notifications/job/progress",
  "params": {
    "eventId": "evt_gg_101",
    "jobId": "giftjob_42",
    "timestamp": "2025-11-21T10:18:30Z",
    "percentage": 40,
    "stage": "Hədiyyələri büdcəyə görə filtr edirik",
    "etaSeconds": 25
  }
}

Burada önəmlidir ki, percentage məntiqli şəkildə 100-ə doğru artsın, ora-bura tullanmasın. Proqres sahəsi üçün bir ad seçin (məsələn, percentage) və onu bütün hadisələrdə istifadə edin. MCP-nin rəsmi proqres utilitində də qayda var: proqres yalnız artır.

Məlumat/resurs yenilənməsi hadisələri (Resource/Data events)

Bəzən istifadəçi üçün konkret jobId o qədər də vacib deyil. Daha önəmlisi odur ki, hansısa kamera dəyişib: məhsul feed-i yenilənib, yeni hesabat snapshot-ı formalaşıb, fərdi profil yenidən generasiya edilib.

MCP-də artıq resources/updated, resources/list_changed və bənzəri səviyyəsində standart bildirişlər var; onlar müştəriyə siqnal verir: “resurs siyahısını yenidən oxu, orada nəsə dəyişib”.

GiftGenius üçün bu belə görünə bilər:

{
  "jsonrpc": "2.0",
  "method": "resources/updated",
  "params": {
    "eventId": "evt_feed_17",
    "timestamp": "2025-11-21T09:00:00Z",
    "resourceId": "resource:product-feed",
    "changeType": "snapshot_ready"
  }
}

Vidcet belə bir hadisə alanda, məsələn, “Hədiyyə siyahısını yenilə” düyməsini vurğulaya bilər.

UX və sistem hadisələri

Biznesdən sərt şəkildə olmayan, amma UX və ya diaqnostika üçün vacib olan hadisələr də var:

  • logging/message log-mesajları — loglar üçün MCP-nin standart bildirişi;
  • heartbeat/ping — serverdən periodik “mən yaşayıram” siqnalları;
  • deqradasiya barədə xəbərdarlıqlar: məsələn, “hazırda xarici API yavaşdır, nəticələr daha gec gələ bilər”.

Bu cür hadisələr monitorinq və sazlama üçün faydalıdır; bəzən UI-də “sakitləşdirib” göstərmək olar ki, sistem ölməyib, sadəcə məşğuldur.

4. Hadisənin strukturu: məcburi sahələr və payload

Hadisə alət sorğusu kimi eyni API obyektidir. Onu layihələndirmək lazımdır. Yaxşı vərdiş — əsas sahələr toplusu barədə razılaşmaqdır.

Konseptual olaraq hadisəni üç hissəyə bölmək faydalıdır: metadat, korrelyasiya və faydalı yük.

Ümumi forma nümunəsi:

{
  "jsonrpc": "2.0",
  "method": "notifications/job/progress",
  "params": {
    "eventId": "evt_gg_103",
    "type": "job.progress",
    "timestamp": "2025-11-21T10:19:00Z",
    "jobId": "giftjob_42",
    "payload": {
      "percentage": 60,
      "stage": "Rəyləri müqayisə edirik",
      "etaSeconds": 15
    }
  }
}

Bu struktura daxilində aşağıdakıları ayırmaq olar:

  • eventId — hadisənin unikal identifikatoru. Müştəridə deduplikasiya üçün lazımdır;
  • type — hadisənin məntiqi adı (method-u təkrarlaya/normalizə edə bilər);
  • timestamp — hadisənin serverdə nə vaxt generasiya olunduğu;
  • jobId və ya digər correlation-id — bu hadisənin nəyə aid olduğunu anlamaq üçün;
  • payload — məhz məlumatların özü. Hər hadisə tipi üçün öz forması var.

Real sistemdə bu strukturları JSON Schema ilə və ya heç olmasa TypeScript tipləri ilə formal şəkildə təsvir etmək istəyəcəksiniz ki, həm server, həm də müştəri mesajları validasiya etsin. Bəzi komandalar bunun üçün CloudEvents-dən ilhamlanan formatdan istifadə edir: orada da id, source, type, time və s. kimi standart sahələr var.

Amma əsas fikir sadədir: hadisə maşın-oxunaqlı və konsistent olmalıdır — sürprizsiz, “bəzən sahənin adı jobId, bəzən job_id, bəzən isə ümumiyyətlə yoxdur” kimi hallarsız.

Aşağıdakı nümunələrdə kodu yükləməmək üçün daha çox “yastılanmış” variantdan istifadə edəcəyik: hadisənin bütün məlumatları birbaşa params daxilində yerləşir, ayrıca payload yoxdur və type sahəsi bəzən buraxılır, çünki onun rolunu method oynayır. Prinsip dəyişmir: hər hadisənin sabit metadatları var (eventId, jobId, timestamp) və qabaqcadan proqnozlaşdırılan faydalı yükü.

5. Hadisələrin idempotentliyi: nə üçün və necə

İndi bu mühazirənin ən vacib sözü — idempotentlik.

Hadisə emalçısının idempotentliyi o deməkdir ki, eyni hadisə bir dəfə və ya on dəfə emal olunsa belə, sistemin yekun vəziyyəti düzgün qalacaq. Şəbəkə və təkrar cəhdlər olan paylanmış sistemlərdə bu, sözün əsl mənasında həyat-memat məsələsidir.

Ümumiyyətlə eyni hadisə niyə bir neçə dəfə gələ bilər?

Səbəblər çoxdur: bağlantıların qırılmasından və yenidən qoşulmalardan tutmuş server tərəfdə “hər ehtimala qarşı” bildirişi yenidən göndərən retry-lərə qədər. Axın protokollarından istifadə edəndə (məsələn, server SSE kimi açıq bağlantıya hadisələri özü push edəndə — bu barədə ayrıca nəqliyyat mühazirəsində danışacağıq) bu klassikadır: müştəri Last-Event-ID ilə yenidən qoşulur, server buraxılmış hadisələri tamamlayır və onlardan bəzilərini müştəri ikinci dəfə görür.

Emalçınız idempotent deyilsə, qəribəliklər başlayır:

  • job.completed hadisəsi bonusların ikiqat hesablanmasına və ya sifariş statusunun iki dəfə dəyişməsinə səbəb olur;
  • resource.updated hadisəsi vidceti hər dəfə “kartları əlavə etməyə” məcbur edir və UI-da dublikatlar yaranır;
  • təkrar job.progress istifadəçiləri qorxudur, əgər proqres zolağı irəli-geri tullanmağa başlayırsa.

Düzgün strategiya iki layda işləyir: serverdə hadisələrin generasiyası və müştəridə onların emalı.

Server tərəfi: sabit id-lər və state machine

Server bunları etməlidir:

  • hər bir məntiqi hadisə üçün unikal eventId generasiya etmək;
  • eyni jobId üçün hadisələrin düzgün vəziyyət ardıcıllığını formalaşdırdığına zəmanət vermək: siz job.completed-dən sonra job.failed və ya fərqli nəticələrlə iki ayrı job.completed göndərə bilməzsiniz.

Yəni faktiki olaraq tapşırıq üçün vəziyyət maşınınız var və hər hadisə icazə verilən keçiddir.

Müştəri tərəfi: deduplikasiya və “yumşaq” yeniləmələr

Müştəri (vidcet, agent və ya başqa komponent) bunları etməlidir:

  • ən azı cari bağlantı/sessiya müddətində artıq emal olunmuş eventId çoxluğunu saxlamaq;
  • emal etməzdən əvvəl yoxlamaq: əgər eventId artıq görünmüşdürsə, sadəcə görməzlikdən gəlmək və ya UI-ı yan effektlərsiz yenidən çəkmək;
  • tapşırığın statusunu dəyişən hadisələri (job.completed, job.failed) alanda keçidin qəbulolunan olduğuna əmin olmaq: məsələn, tapşırıq artıq completed kimi işarələnibsə, təkrar job.completed heç nəyi dəyişməməlidir, failed isə ümumiyyətlə səhv kimi görməzlikdən gəlinməlidir.

Commerce dünyasından klassik nümunə: ödənişin təsdiqi webhook-unun emalı. Eyni order.paid asanlıqla iki dəfə gələ bilər; buna görə backend paymentId və “artıq hesablanıb” bayrağını saxlayır. Webhook ikinci dəfə gəlsə belə, sifarişin vəziyyəti dəyişmir. MCP hadisələrini də eyni düşüncə tərzi ilə layihələndirmək lazımdır.

6. Nümunə: GiftGenius üçün hadisələri layihələndiririk

Bunu bizim tədris GiftGenius-umuza tətbiq edək. Uzun bir ssenari təsəvvür edin: istifadəçi əməkdaş siyahısı və maraqları olan böyük CSV faylını yüklədi və “hamı üçün hədiyyə ideyaları seç” dedi. Əməliyyat onlarla saniyə çəkə bilər.

Məntiqli hadisə modelini belə təsvir etmək olar:

  1. İstifadəçi start_bulk_gift_analysis alətini işə salır. Alət jobId qaytarır: "bulk_2025_001".
  2. MCP server tapşırığı yaradır və demək olar dərhal qısa təsvirlə job.started göndərir.
  3. Yerinə yetirilmə zamanı bir neçə job.progress mərhələsi göndərilir:
    • 10% — “Faylı pars edirik və formatı yoxlayırıq”;
    • 40% — “Maraqları və departamentləri çıxarırıq”;
    • 70% — “Hədiyyələri kateqoriyalar üzrə uyğunlaşdırırıq”;
    • 100% — tamamlanmadan dərhal əvvəl.
  4. Sonda yekun tövsiyələr olan resursa istinadla job.completed gəlir.
  5. Əgər hər şey pis gedərsə — completed əvəzinə xətakodu və bəlkə də düzəltmə məsləhəti ilə job.failed gəlir.

Qeyri-rəsmi olaraq hər şey belə olacaq, amma gəlin bunu job.progressjob.completed üçün JSON-sxem şəklində sabitləyək. Psevdo-JSON Schema (sadələşdirilmiş):

{
  "job.progress": {
    "type": "object",
    "properties": {
      "eventId": { "type": "string" },
      "jobId": { "type": "string" },
      "timestamp": { "type": "string", "format": "date-time" },
      "percentage": { "type": "number", "minimum": 0, "maximum": 100 },
      "stage": { "type": "string" },
      "etaSeconds": { "type": "number" }
    },
    "required": ["eventId", "jobId", "timestamp", "percentage", "stage"]
  }
}
{
  "job.completed": {
    "type": "object",
    "properties": {
      "eventId": { "type": "string" },
      "jobId": { "type": "string" },
      "timestamp": { "type": "string", "format": "date-time" },
      "summary": { "type": "string" },
      "resultResourceId": { "type": "string" }
    },
    "required": ["eventId", "jobId", "timestamp", "resultResourceId"]
  }
}

Hazırda tam sxem validasiyası reallaşdırmaq məcburiyyətində deyilsiniz, amma belə bir strukturu zehində saxlamaq faydalıdır: bu, sahələrin “yayılıb getməsinin” qarşısını alır və vacib metadataları unutmağa imkan vermir.

7. Mini-praktika: MCP hadisələri göndərən server

İndi nəzəriyyəni kiçik bir TypeScript psevdo-kodu ilə birləşdirək. Hələ real MCP kitabxanalarına girməyəcəyik (birincisi, onlar hələ də inkişaf edir, ikincisi, fokus modeldədir), amma struktur skelet çəkəcəyik.

Gəlin fərz edək ki, MCP serverimizdə sendNotification abstraksiyası var və o, JSON-RPC notification-ı yenidən ChatGPT-yə göndərməyi bacarır. Psevdo-interfeys:

// MCP notification göndərmək üçün utilit
async function sendNotification(
  method: string,
  params: Record<string, unknown>
) {
  // Burada JSON-u serializə edib aktiv MCP bağlantısı ilə göndərərdiniz
}

İndi start_bulk_gift_analysis alətinin emalçısını reallaşdıraq. O, tapşırığı qeydiyyata alır, jobId qaytarır, fonda isə “tik-tik” edib proqres göndərir. Real həyatda bu, worker və növbə olardı, amma hələlik taymerlə kifayətlənək.

type Job = {
  id: string;
  status: "pending" | "running" | "completed" | "failed";
};

const jobs = new Map<string, Job>();

export async function startBulkGiftAnalysisTool() {
  const jobId = `bulk_${Date.now()}`;
  jobs.set(jobId, { id: jobId, status: "pending" });

  // Dərhal job.started göndəririk
  await sendNotification("notifications/job/started", {
    eventId: `evt_${jobId}_started`,
    jobId,
    timestamp: new Date().toISOString(),
    summary: "Böyük hədiyyə siyahısının analizi işə salındı"
  });

  simulateJob(jobId); // tapşırığı fonda “işə salırıq”

  return { jobId };
}

Tapşırığın özünün simulyasiyası:

async function simulateJob(jobId: string) {
  jobs.set(jobId, { id: jobId, status: "running" });

  const stages = [
    { percent: 10, stage: "CSV-ni pars edirik" },
    { percent: 40, stage: "Maraqları təhlil edirik" },
    { percent: 70, stage: "Hədiyyələri seçirik" },
    { percent: 100, stage: "Nəticəni formalaşdırırıq" }
  ];

  for (const s of stages) {
    await sendNotification("notifications/job/progress", {
      eventId: `evt_${jobId}_${s.percent}`,
      jobId,
      timestamp: new Date().toISOString(),
      percentage: s.percent,
      stage: s.stage
    });
    await new Promise(r => setTimeout(r, 1000));
  }

  jobs.set(jobId, { id: jobId, status: "completed" });

  await sendNotification("notifications/job/completed", {
    eventId: `evt_${jobId}_done`,
    jobId,
    timestamp: new Date().toISOString(),
    summary: "Hədiyyə analizi tamamlandı",
    resultResourceId: `resource:gifts:${jobId}`
  });
}

Kod qəsdən sadədir, amma onda yaxşı görünür:

  • startedprogress* → completed hadisə ardıcıllığından istifadə edirik;
  • hər hadisə unikal eventId alır;
  • bütün hadisələr eyni jobId-a bağlanır.

Gələcəkdə real növbələr və worker-lər əlavə edəndə hadisələrin strukturu təxminən eyni qalacaq — yalnız sendNotification-ın harada çağırıldığı dəyişəcək.

8. Müştəri: ən sadə idempotent hadisə emalçısı

Müştəri tərəfində (məsələn, Apps SDK vidcetinizdə) belə hadisələri qəbul etməyi, onları cari tapşırıqlarla bağlamağı və dublikatlardan dəli olmamağı öyrənmək lazımdır.

Hələ nəqliyyata (bunu sonra edəcəyik) dalmadan təsəvvür edək ki, hər daxil olan notification zamanı MCP-müştəri qatınız onMcpNotification funksiyasını çağırır.

Ən sadə deduplikasiyanı əlavə edək:

const processedEvents = new Set<string>();

function handleNotification(method: string, params: any) {
  const eventId = params.eventId as string | undefined;
  if (!eventId) return; // çox mübahisəlidir, amma nümunə üçün uyğundur

  if (processedEvents.has(eventId)) {
    // Təkrar — görməzlikdən gəlirik və ya UI-ı yumşaq şəkildə yeniləyirik
    return;
  }
  processedEvents.add(eventId);

  if (method === "notifications/job/progress") {
    updateJobProgress(params.jobId, params.percentage, params.stage);
  } else if (method === "notifications/job/completed") {
    markJobCompleted(params.jobId, params.resultResourceId);
  }
}

updateJobProgressmarkJobCompleted reallaşdırılması isə artıq saf React/UI kodudur:

function updateJobProgress(jobId: string, percent: number, stage: string) {
  // məsələn, Zustand/Redux/React state-ə qoyuruq
  console.log(`Job ${jobId}: ${percent}% — ${stage}`);
}

function markJobCompleted(jobId: string, resourceId: string) {
  console.log(`Job ${jobId} tamamlandı, resurs: ${resourceId}`);
}

Belə emalçı:

  • hadisə iki dəfə gəlsə də pozulmur;
  • yan effektlər etmir (məsələn, “Hazırdır!” modalını ikinci dəfə göstərmək kimi);
  • daha mürəkkəb məntiqə, məsələn, qəbul edilən vəziyyət keçidlərinin validasiyasına yol açır (artıq completed üzərinə failed verməyə imkan verməmək).

Döyüş kodunda yəqin ki, MCP serverinə yenidən qoşulanda processedEvents-i sıfırlamaq, həmçinin yalnız eventId yox, hər jobId-ın cari statusunu da saxlamaq istəyəcəksiniz ki, qəribə hadisə ardıcıllığında daha ağıllı davranasınız.

Sonra başa düşmək vacibdir ki, bu MCP hadisələri agent/vidcetdən keçərək necə konkret istifadəçi təcrübəsinə çevrilir: proqres barı, icra mərhələləri, yekun nəticələrin yaranması. Gəlin hadisələri run/workflow və UX ilə bağlayaq.

9. Hadisələrin run/workflow və UX ilə bağlanması

Workflow və agentlər barədə tam modul artıq var idi, indi isə bütöv mənzərəni görəcəksiniz. Biz artıq hadisə ailələrini (job.*, resource.*, sistem) təqdim etdik; gəlin onların agent/vidcet və ChatGPT-dən necə keçərək konkret istifadəçi təcrübəsinə çevrildiyinə baxaq.

Uzunmüddətli tapşırıqla tipik ssenari belə görünür: ChatGPT MCP alətini çağırır, jobId alır; sonra bu jobId üzrə server proqres, tamamlanma və ya xətaya dair hadisələr göndərir; vidcetiniz və ya agent məntiqiniz onların əsasında UI-ı yeniləyir və qərarlar verir.

Ardıcıllıq diaqramında bunu belə çəkmək olar:

sequenceDiagram
    participant User as Istifadəçi
    participant GPT as ChatGPT (model)
    participant App as GiftGenius MCP serveri
    participant Widget as GiftGenius vidceti

    User->>GPT: "2000 əməkdaş üçün hədiyyələr seç"
    GPT->>App: tools.call start_bulk_gift_analysis
    App-->>GPT: response { jobId: "bulk_2025_001" }

    GPT->>Widget: ToolOutput { jobId }
    Widget->>Widget: Proqres zolağını göstər

    App-->>GPT: notification job.started
    App-->>GPT: notification job.progress (10%, 40%, 70%, 100%)
    App-->>GPT: notification job.completed { resultResourceId }

    GPT->>Widget: Hadisələri/məlumatı vidcetə ötürür
    Widget->>User: Proqresi yeniləyir və nəticəni göstərir
    

Praktikada real diaqram bir qədər mürəkkəb olacaq, amma əsas fikir sadədir: MCP hadisələri fon əməliyyatlarınız ilə istifadəçi təcrübəsi arasında “sinir sistemidir”.

10. MCP hadisələri ilə işləyərkən tipik səhvlər

Xəta №1: “Hadisə = prodakşn formatında log”.
Bəzən developer-lər sadəcə əvvəl console.log-a yazdıqlarını MCP-yə ötürməklə başlayırlar. Nəticədə hadisələrdə nə eventId, nə jobId, nə də normal timestamp olur — yalnız “demək olar bitirdik” tipli yarı-şeir mesajları. Bu yanaşma sistemi kövrək edir: onları parse etmək çətindir, deduplikasiya mümkün deyil, UI mesajın hansı tapşırığa aid olduğunu bilmir. Ən yaxşısı elə əvvəlcədən hadisələri rəsmi müqavilə kimi layihələndirməkdir: dəqiq metod adı, sabit sahələr toplusu, məntiqli payload.

Xəta №2: Idempotentliyin və unikal eventId-in olmaması.
Bir çoxları sadəlövh ideya ilə başlayır: “nə olsun ki, hadisələr axı bir dəfə gəlir”. Bir həftə sonra başlayır: müştəri yenidən qoşulanda bildirişlər dublikatlanır, istifadəçi eyni şeyi iki dəfə alır, kommersiya backend-i bonusları iki dəfə hesablayır. Unikal eventId və müştəri tərəfində elementar deduplikasiya olmadan gec-tez ciddi bug tutacaqsınız. Paylanmış sistemdə “at-least-once delivery” modelindən çıxış etmək lazımdır: dublikatlar qaçılmazdır.

Xəta №3: Sistem və biznes hadisələrini bir qazana qatmaq.
Məsələn, eyni axına logging/message, job.progress, job.completed, resources/updated yağır və bunların hamısı aydın type/method sərhədləri olmadan. Nəticədə UI qatı tapşırığın bitdiyini anlamaq üçün if (message.includes("hazırdır")) kimi qəribə şeylər etməyə başlayır. Yaxşısı aydın ayırmaqdır: sistem bildirişləri (loglar, heartbeat) və sərt şəkildə təsvir edilmiş sxemləri olan biznes hadisələr (job.*, resource.*) var.

Xəta №4: Tapşırıq vəziyyəti keçidlərinin ardıcıllıqdan çıxması.
Elə olur ki, server bir axında əvvəlcə job.completed, sonra qəfil job.progress, ardınca job.failed göndərir. Bu, aydın state machine və hadisə buraxılışı zamanı yoxlamalar olmadıqda baş verir. Müştərilərə nə baş verdiyini anlamaq mümkün olmur. Düzgünü sonlu vəziyyət avtomatını təsvir etmək və onu pozan hadisələri buraxmamaqdır: məsələn, completed-dən sonra maksimum əlavə informasiya hadisəsi göndərmək olar, amma tapşırığı geri running-ə keçirmək olmaz.

Xəta №5: Mövcud spekin konkret MCP metod adlarına sərt bağlama.
MCP spesifikasiyası hələ inkişaf edir. Hər şeyi cari sistem adlarına sıx bağlasanız və öz namespacelərinizi nəzərdə tutmasanız, protokoldakı istənilən dəyişiklik sistemi yenidən yazmağa məcbur edəcək. Hadisələri MCP-nin üzərində öz mini-spekiniz kimi qəbul etmək daha yaxşıdır: mövcud metodlara (notifications/progress, resources/updated) əsaslana bilərsiniz, amma biznes hadisələri (notifications/job/*)ni öz ad sahənizdə layihələndirib nisbətən müstəqil saxlamaq lazımdır.

Xəta №6: Hadisələrin UX ilə əlaqəsinin olmaması.
Bəzən komanda backend-də gözəl hadisə modeli qurur, amma onu vidcetə çatdırmır: job.progress loqlarda “var”, UI isə 40 saniyəlik tənha spinner göstərir. Belə ssenaridə istifadəçi nə MCP-yə, nə də süni intellektə inanır. Hadisələri layihələndirərkən həmişə hansı konkret UI effekti istədiyinizi düşünün: proqres bar, mərhələlər, qismən nəticələr. MCP hadisələri protokol xətrinə yox, tətbiqin anlaşılan davranışı xətrinə lazımdır.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION