Skip to main content
Glama
melihkizmaz

agent-native-ops-demo

by melihkizmaz
README.md
# agent-native-ops-demo

**Agent-Native Operations** yazı serisinin demo reposu — MCP **2026-07-28** spec'ine göre, NestJS + [MCP-Nest](https://github.com/rekog-labs/MCP-Nest) ile yazılmış stateless bir MCP server.

> **Donmuş snapshot:** Bu repo, serinin yazıları için tarihli bir kanıt ortamıdır; MCP 2026-07-28 revizyonu ve `@rekog/mcp-nest@2.0.0` esas alınmıştır. Spec değiştikçe **güncel tutulmayacaktır** — her yazının changelog'u hangi tag'e dayandığını söyler.

## Ne gösteriyor

| Tool | Ne kanıtlıyor |
|---|---|
| `echo_upper` | Saf stateless tool — replica ölçekleme ölçümlerinin yükü |
| `search_start` / `search_next` | Server-minted **handle** (Redis, `h1_` prefix, 15 dk TTL) — "state nereye gitti"; expire olan handle `handle_expired` + `recovery` döner |
| `create_invoice_naive` | Mutlu yolda doğru, retry'da **duplicate üreten** bilerek-naif tool |
| `create_invoice` | `idempotency_key` ile bayt-bayt **replay** — retry baş tacı |
| `slow_report` | Bilerek yavaş, progress bildiren tool — pod-kill / kopan-stream deneylerinin hedefi |

Sunucu **dual-era** çalışır (`protocol: 'dual'`): aynı `/mcp` endpoint'i hem eski protokolü (initialize + session) hem stateless 2026-07-28'i cevaplar. `tools/list` cevabı SEP-2549 gereği `ttlMs: 3600000, cacheScope: "public"` taşır (`cacheHints` ile ayarlı; varsayılan `ttlMs: 0, private`).

## Hızlı başlangıç

Gereksinimler: Node 22+, Redis (lokal `:6379` ya da `docker compose up -d`), `REDIS_URL` ile değiştirilebilir.

```bash
npm install
npm run build
npm start          # :3000/mcp (MCP), :3000/healthz (liveness)
```

### Duman testi (modern era, 2026-07-28)

Her istek kendi kendine yeterlidir: `_meta` içinde `protocolVersion` + `clientCapabilities` taşır, `Mcp-Method` header'ı zorunludur (eksiğinde sunucu hangi alanın eksik olduğunu söyleyen `-32602` döner).

```bash
curl -s -X POST localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Mcp-Method: tools/list' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{
    "io.modelcontextprotocol/protocolVersion":"2026-07-28",
    "io.modelcontextprotocol/clientInfo":{"name":"smoke","version":"0.0.1"},
    "io.modelcontextprotocol/clientCapabilities":{}}}}'
```

İdempotency replay'ini görmek için aynı `idempotency_key` ile `create_invoice`'u iki kez çağırın (`Mcp-Method: tools/call` + `Mcp-Name: create_invoice`) ve `redis-cli get invoice:count` ile sayacı izleyin: key'li tool'da sayaç 1'de kalır, `create_invoice_naive`'de her çağrı sayacı artırır.

Alternatif: [MCP Inspector](https://github.com/modelcontextprotocol/inspector) ile `http://localhost:3000/mcp`'ye bağlanın.

## Kanıt katmanı

Her tool çalıştırması benzersiz bir `executionId` ile hem stdout'a (JSON log) hem Redis `exec-log` listesine yazılır. Retry'lar protokol seviyesinde **yeni request-id** taşıdığı için (SSE resumability kaldırıldı), duplicate sayımı protokole değil bu kayıtlara yaslanır.

## Yol haritası (serinin ölçüm parçaları)

- [x] Parça 1 — NestJS + MCP-Nest iskeleti: 5 tool, Redis handle store, idempotency, execution log
- [x] Parça 2 — k8s/kind manifest'leri: 1↔3 replica, NodePort Service, `terminationGracePeriodSeconds`, chaos endpoint'i
- [x] Parça 3 — k6 senaryoları + koşulmuş deneyler (aşağıda)
- [ ] Parça 4 — cache katmanı deneyi: `ttlMs` + paylaşımlı cache, origin-hit sayımı
- [ ] Parça 5 — OTel GenAI facade + trace waterfall (serinin OTEL-GENAI yazısı)

## Deneyler (Parça 2-3)

Ortam: lokal **kind** cluster'ı (tek node, Docker Desktop, Apple Silicon); k6 aynı makinede. Kurulum:

```bash
docker build -t agent-native-ops-demo:0.1.0 .
kind create cluster --config kind-config.yaml
kind load docker-image agent-native-ops-demo:0.1.0 --name anops
kubectl apply -f k8s/
```

**Sabit yük (1 vs 3 replica, `echo_upper`):** `k6 run -e RATE=100 -e DURATION=60s k6/steady-load.js`

| Yük | 1 replica p95 / p99 | 3 replica p95 / p99 |
|---|---|---|
| 100 rps · 60 sn | 6.7 ms / 21.1 ms | 7.4 ms / 30.8 ms |
| 600 rps · 45 sn | 14.2 ms / 51.6 ms | 16.2 ms / 52.4 ms |

Bu iş yükünde yatay ölçeklemenin latency getirisi yok — kazanç kapasite değil, dayanıklılık (aşağıda).

**Pod-kill duplicate deneyi:** 30 rps fatura niyeti, 90 sn, `delay_ms=500` (commit-sonrası cevap penceresi); koşu sırasında 8 kez `POST /chaos/crash` ile rastgele pod'lar düşürüldü. Client, 2026-07-28'in zorunlu kıldığı gibi kopan isteği yeni request-id ile tekrar gönderiyor (`k6/pod-kill.js`).

| Koşu | Niyet | Retry | Duplicate fatura | Oran |
|---|---|---|---|---|
| `create_invoice_naive` | 2 700 | 132 | **37** | **%1.37** |
| `create_invoice` (idempotency_key) | 2 108 | 1 121 | **1** | **%0.05** |

Sekiz crash'e rağmen kaybolan niyet: naifte 0/2700, key'lide 1/2108 — servis 3 replica ile ayakta kaldı. Key'li koşudaki tek duplicate, crash'in iş commit'i ile idempotency kaydının *arasına* denk geldiği vaka: read-then-write idempotency atomik değildir; gerçek exactly-once için ikisini tek transaction'a koyun (serinin tool-contract yazısının konusu). Analiz: `kubectl exec deploy/redis -- redis-cli lrange exec-log 0 -1 | python3 scripts/analyze-duplicates.py <run-prefix>`; ham k6 çıktıları `results/` altında.

## Seri

1. NestJS ile Production'da Stateless MCP Server *(bu repo'nun ana yazısı)*
2. Agent'lara API Tasarlamak: Retry'a Dayanıklı MCP Tool Contract'ları
3. AI Agent'lara Internal Developer Platform'da Kimlik Vermek
4. Node.js'te OpenTelemetry GenAI ile AI Agent Instrumentasyonu
5. Auto-Remediation Incident'ı Ne Zaman Büyütür

*(Yazı linkleri yayınlandıkça eklenecek.)*