first commit

This commit is contained in:
2026-03-24 16:24:16 +03:00
commit 28c24d8cb1
20 changed files with 4271 additions and 0 deletions
+701
View File
@@ -0,0 +1,701 @@
# 🌩️ iklim.co MCP Server
[Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for the iklim.co Weather API. AI asistanların (Claude, OpenClaw vb.) iklim.co'nun hava durumu, yıldırım, fırtına, yağış ve alarm API'lerine doğal dil ile erişmesini sağlar.
## 📋 İçindekiler
- [🌐 Genel Bakış](#-genel-bakış)
- [⚙️ Gereksinimler](#️-gereksinimler)
- [🚀 Kurulum](#-kurulum)
- [🔧 Ortam Değişkenleri](#-ortam-değişkenleri)
- [▶️ Build ve Çalıştırma](#️-build-ve-çalıştırma)
- [🔌 MCP Client Konfigürasyonu](#-mcp-client-konfigürasyonu)
- [Claude CLI (.mcp.json)](#claude-cli-mcpjson)
- [OpenClaw](#openclaw)
- [Diğer MCP İstemcileri](#diğer-mcp-i̇stemcileri)
- [🛠️ Araç Kataloğu](#️-araç-kataloğu)
- [⚡ Yıldırım / Lightning](#-yıldırım--lightning)
- [🌪️ Fırtına / Thunderstorm](#️-fırtına--thunderstorm)
- [🌧️ Yağış / Precipitation](#️-yağış--precipitation)
- [🌤️ Hava Tahmini / Forecast](#️-hava-tahmini--forecast)
- [👤 Auth & Kullanıcı](#-auth--kullanıcı)
- [🏢 Hesap / Account](#-hesap--account)
- [📍 Nokta Alarmları / Point Alarms](#-nokta-alarmları--point-alarms)
- [🗺️ Coğrafi Alarmlar / Geo Alarms](#️-coğrafi-alarmlar--geo-alarms)
- [📅 Tahmin Alarmları / Forecast Alarms](#-tahmin-alarmları--forecast-alarms)
- [🏗️ Mimari](#️-mimari)
- [🔐 Kimlik Doğrulama ve Güvenlik](#-kimlik-doğrulama-ve-güvenlik)
- [Dahili Auth Akışı](#dahili-auth-akışı)
- [JWT Token Yaşam Döngüsü](#jwt-token-yaşam-döngüsü)
- [HTTP İstek Header'ları](#http-i̇stek-headerları)
- [HMAC-SHA256 İmza Hesabı](#hmac-sha256-i̇mza-hesabı)
- [Auth ile Normal İstekler Arasındaki Fark](#auth-ile-normal-i̇stekler-arasındaki-fark)
- [Güvenlik Önerileri](#güvenlik-önerileri)
- [💻 Geliştirici Notları](#-geliştirici-notları)
---
## 🌐 Genel Bakış
Bu MCP server, iklim.co REST API'sinin tüm yeteneklerini **57 araç** (tool) olarak sunar. Araçlar 9 kategoriye ayrılmıştır:
| Kategori | Araç Sayısı | Kapsam |
|----------|:-----------:|--------|
| ⚡ Lightning | 2 | Yıldırım çarpma verileri |
| 🌪️ Thunderstorm | 3 | Fırtına hücresi takibi |
| 🌧️ Precipitation | 2 | Radar yağış verileri |
| 🌤️ Forecast | 3 | Saatlik / günlük / anlık tahmin |
| 👤 Auth & User | 11 | Kimlik doğrulama, kullanıcı yönetimi |
| 🏢 Account | 8 | Hesap ve abonelik yönetimi |
| 📍 Point Alarms | 6 | Nokta tabanlı uyarı abonelikleri |
| 🗺️ Geo Alarms | 12 | Coğrafi sınır bazlı uyarılar + il/ilçe/mahalle kataloğu |
| 📅 Forecast Alarms | 10 | Eşik bazlı tahmin uyarıları + il/ilçe kataloğu |
| **Toplam** | **57** | |
---
## ⚙️ Gereksinimler
- **Node.js** >= 18 (ES2022 desteği gerekli)
- **npm** >= 9
- iklim.co API erişim bilgileri (HMAC secret, kullanıcı adı ve şifre)
---
## 🚀 Kurulum
```bash
cd mcp-server
npm install
npm run build
```
---
## 🔧 Ortam Değişkenleri
Server başlamadan önce aşağıdaki değişkenlerin tanımlı olması gerekir. Geliştirme ortamında `.env` dosyası oluşturabilirsiniz (projenin `.gitignore` dosyasına ekli):
```bash
# .env
IKLIM_ENV=test # prod | test | local (IKLIM_BASE_URL yoksa kullanılır)
IKLIM_BASE_URL= # Opsiyonel. Tanımlıysa IKLIM_ENV'i override eder
IKLIM_HMAC_SECRET=<secret> # Zorunlu. İstek imzalama için HMAC-SHA256 anahtarı
IKLIM_USERNAME=<email> # Zorunlu. API kullanıcı e-postası
IKLIM_PASSWORD=<password> # Zorunlu. API kullanıcı şifresi
```
**🌍 Ortama göre base URL:**
| `IKLIM_ENV` | URL |
|-------------|-----|
| `prod` | `https://api.iklim.co` |
| `test` | `https://api-test.iklim.co` |
| `local` | `http://localhost:8080` |
---
## ▶️ Build ve Çalıştırma
```bash
# TypeScript'i derle (dist/ klasörünü oluşturur)
npm run build
# Derlenmiş server'ı başlat
npm start
# Geliştirme modunda çalıştır (derleme gerekmez, ts-node kullanır)
npm run dev
```
Başarılı başlatmada çıktı:
```
iklim.co MCP server running
```
> Server **stdio** transportu üzerinden iletişim kurar — doğrudan terminal ile çalıştırmak yerine bir MCP istemcisi tarafından yönetilmesi beklenir.
---
## 🔌 MCP Client Konfigürasyonu
### Claude CLI (.mcp.json)
Projenin kök dizinindeki `.mcp.json` dosyası Claude CLI tarafından otomatik olarak yüklenir:
```json
{
"mcpServers": {
"iklim": {
"command": "node",
"args": ["/tam/yol/mcp-server/dist/index.js"],
"env": {
"IKLIM_ENV": "test",
"IKLIM_HMAC_SECRET": "<secret>",
"IKLIM_USERNAME": "<email>",
"IKLIM_PASSWORD": "<password>"
}
}
}
}
```
Global olarak tanımlamak için `~/.claude/settings.json` içine aynı `mcpServers` bloğunu ekleyin.
### OpenClaw
`openclaw mcp set` komutu `env` parametresini desteklemez. Tüm alanlar tek satır JSON olarak geçirilmeli, `env` de dahil:
```bash
openclaw mcp set iklim '{"type":"stdio","command":"node","args":["/tam/yol/mcp-server/dist/index.js"],"env":{"IKLIM_ENV":"test","IKLIM_HMAC_SECRET":"<secret>","IKLIM_USERNAME":"<email>","IKLIM_PASSWORD":"<password>"}}'
```
Veya `~/.openclaw/openclaw.json` içinde `mcp` bölümüne doğrudan ekleyin (okunabilir format):
```json
{
"mcp": {
"iklim": {
"type": "stdio",
"command": "node",
"args": ["/tam/yol/mcp-server/dist/index.js"],
"env": {
"IKLIM_ENV": "test",
"IKLIM_HMAC_SECRET": "<secret>",
"IKLIM_USERNAME": "<email>",
"IKLIM_PASSWORD": "<password>"
}
}
}
}
```
### Diğer MCP İstemcileri
MCP stdio standardını destekleyen her istemci kullanılabilir. Gerekli bilgiler:
- **transport**: `stdio`
- **command**: `node`
- **args**: `["<dist/index.js tam yolu>"]`
- **env**: Yukarıdaki dört değişken
---
## 🛠️ Araç Kataloğu
### ⚡ Yıldırım / Lightning
#### `get_lightnings_within`
Belirli bir merkez noktası ve yarıçap içindeki yıldırım çarpmalarını sorgular.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `latitude` | number | Merkez enlem (-90 / 90) |
| `longitude` | number | Merkez boylam (-180 / 180) |
| `radius` | number | Yarıçap, metre (0 – 50.000) |
| `backwardInterval` | number | Geriye dönük süre, saniye (60 – 2.592.000 / 30 gün) |
| `endTimeEpoch` | number | Sorgu bitiş zamanı, epoch ms |
| `pageNumber` | number? | Sayfa numarası (default: 0) |
| `pageSize` | number? | Sayfa boyutu, max 100 (default: 10) |
#### `get_lightnings_page`
Zaman aralığına göre yıldırım verilerini sayfalı olarak getirir.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `backwardInterval` | number | Geriye dönük süre, saniye (60 – 432.000 / 5 gün) |
| `endTimeEpoch` | number | Sorgu bitiş zamanı, epoch ms |
| `pageNumber` | number? | Sayfa numarası |
| `pageSize` | number? | Sayfa boyutu, max 100 |
---
### 🌪️ Fırtına / Thunderstorm
#### `get_thunderstorms_within`
Yarıçap içindeki fırtına hücrelerini sorgular.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `latitude` | number | Merkez enlem |
| `longitude` | number | Merkez boylam |
| `radius` | number | Yarıçap, metre (0 – 50.000) |
| `backwardInterval` | number | Geriye dönük süre, saniye (60 – 2.592.000) |
| `endTimeEpoch` | number | Bitiş zamanı, epoch ms |
| `intersectsWith` | string? | `THREAT_POLYGON` veya `CELL_POLYGON` |
| `pageNumber` | number? | |
| `pageSize` | number? | |
#### `get_thunderstorms_page`
Zaman aralığına göre fırtına verilerini sayfalı getirir.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `backwardInterval` | number | Geriye dönük süre, saniye (60 – 432.000) |
| `endTimeEpoch` | number | Bitiş zamanı, epoch ms |
| `pageNumber` | number? | |
| `pageSize` | number? | |
#### `get_thunderstorm_details`
Belirli bir fırtına olayının geçmiş detaylarını getirir.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `eventId` | string | Örn: `EVT20240413001` |
| `pageNumber` | number? | |
| `pageSize` | number? | |
---
### 🌧️ Yağış / Precipitation
#### `get_precipitations_within`
Dairesel alan içindeki radar yağış verilerini sorgular.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `latitude` | number | Merkez enlem |
| `longitude` | number | Merkez boylam |
| `radius` | number | Yarıçap, metre (0 – 50.000) |
| `backwardInterval` | number | Geriye dönük süre, saniye (60 – 2.592.000) |
| `endTimeEpoch` | number | Bitiş zamanı, epoch ms |
| `intensityThreshold` | string? | Min. yoğunluk: `DRIZZLE` < `LIGHT` < `MODERATE` < `HEAVY` < `VERY_HEAVY` < `EXTREME` |
| `pageNumber` | number? | |
| `pageSize` | number? | |
#### `get_precipitations_page`
Zaman aralığına göre yağış verilerini sayfalı getirir. `intensityThreshold` **zorunludur**.
---
### 🌤️ Hava Tahmini / Forecast
#### `get_hourly_forecast`
Saatlik hava durumu tahminlerini getirir (1–14 gün).
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `latitude` | number | Enlem |
| `longitude` | number | Boylam |
| `forecastDays` | number? | 1–14, default 7 |
| `metrics` | string[]? | İstenilen metrik listesi (bkz. aşağısı) |
| `startTime` | string? | ISO 8601 başlangıç zamanı |
| `solarPanelTiltForRadiation` | number? | Panel eğim açısı (radyasyon metriği için) |
| `solarPanelAzimuthForRadiation` | number? | Panel azimut açısı |
<details>
<summary>📊 Desteklenen metrikler (53 adet)</summary>
`WEATHER_ICON`, `TEMPERATURE`, `APPARENT_TEMPERATURE`, `DEW_POINT_TEMPERATURE`, `HUMIDITY`, `CLOUD_COVER`, `CLOUD_COVER_LOW`, `CLOUD_COVER_MID`, `CLOUD_COVER_HIGH`, `WIND_SPEED`, `WIND_GUST`, `WIND_DIRECTION`, `WIND_SPEED_AT_100M`, `WIND_DIRECTION_AT_100M`, `PRECIPITATION`, `RAIN`, `SHOWERS`, `SNOWFALL`, `SNOW_DEPTH`, `PRECIPITATION_PROBABILITY`, `WEATHER_CODE`, `PRESSURE_MSL`, `SURFACE_PRESSURE`, `VISIBILITY`, `EVAPOTRANSPIRATION`, `ET0_FAO_EVAPOTRANSPIRATION`, `VAPOUR_PRESSURE_DEFICIT`, `CAPE`, `LIFTED_INDEX`, `CONVECTIVE_INHIBITION`, `SUNSHINE_DURATION`, `SHORTWAVE_RADIATION`, `DIRECT_RADIATION`, `DIFFUSE_RADIATION`, `DIRECT_NORMAL_IRRADIANCE`, `GLOBAL_TILTED_IRRADIANCE`, `TERRESTRIAL_RADIATION`, `SHORTWAVE_RADIATION_INSTANT`, `DIRECT_RADIATION_INSTANT`, `DIFFUSE_RADIATION_INSTANT`, `DIRECT_NORMAL_IRRADIANCE_INSTANT`, `GLOBAL_TILTED_IRRADIANCE_INSTANT`, `TERRESTRIAL_RADIATION_INSTANT`, `SOIL_TEMPERATURE_0CM`, `SOIL_TEMPERATURE_6CM`, `SOIL_TEMPERATURE_18CM`, `SOIL_TEMPERATURE_54CM`, `SOIL_MOISTURE_0_TO_1CM`, `SOIL_MOISTURE_1_TO_3CM`, `SOIL_MOISTURE_3_TO_9CM`, `SOIL_MOISTURE_9_TO_27CM`, `SOIL_MOISTURE_27_TO_81CM`, `IS_DAY`
</details>
#### `get_daily_forecast`
Günlük agregat tahminleri getirir. Parametreler `get_hourly_forecast` ile aynıdır (`solarPanel*` parametreleri hariç).
#### `get_current_weather`
Konum için anlık (en güncel) hava verilerini getirir.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `latitude` | number | Enlem |
| `longitude` | number | Boylam |
| `metrics` | string[]? | Yukarıdaki metrik listesinden seçim |
---
### 👤 Auth & Kullanıcı
#### `auth_register`
Yeni kullanıcı kaydı oluşturur.
| Parametre | Tip |
|-----------|-----|
| `username` | string (e-posta) |
| `password` | string |
| `firstName` | string |
| `lastName` | string |
| `midName` | string? |
| `locale` | string? |
| `timezone` | string? |
#### `auth_logout`
Geçerli JWT token'ı geçersiz kılar.
#### `user_get_me`
Oturum açmış kullanıcının profilini getirir.
#### `user_get`
`userId` ile kullanıcı detayını getirir.
#### `user_create`
_(Admin)_ Yeni kullanıcı oluşturur. `roles` ve `status` zorunludur.
#### `user_update`
_(Admin)_ `userId` ile kullanıcı alanlarını günceller.
#### `user_list`
Kullanıcı listesini sayfalı getirir. `roles`, `status`, `pageNumber`, `pageSize` ile filtrelenir.
#### `user_unblock`
Bloke edilmiş kullanıcıyı açar.
#### `user_change_password`
`oldPassword` ve `newPassword` ile şifre değiştirir.
#### `user_password_reset_request`
Şifre sıfırlama e-postası gönderir. `userName` ve `passwordResetPageLink` gerekir.
#### `user_password_reset`
Sıfırlama token'ı ile şifre günceller. `userId`, `token`, `newPassword`, `loginPageLink` gerekir.
---
### 🏢 Hesap / Account
#### `account_get`
`userId` ile hesap detaylarını getirir.
#### `account_create`
Yeni hesap oluşturur.
| Parametre | Tip | Değerler |
|-----------|-----|----------|
| `type` | string | `INDIVIDUAL` \| `ORGANIZATION` |
| `subscriptionPlan` | string | `NONE` \| `TRIAL` \| `BASIC_MONTHLY` \| `BASIC_YEARLY` \| `PREMIUM_MONTHLY` \| `PREMIUM_YEARLY` \| `CUSTOM` |
| `mobilePhoneNumber` | string? | |
| `location` | string? | |
| `company` | string? | |
| `industry` | string? | |
| `profilePictureUrl` | string? | |
#### `account_update`
`accountId` ile hesap alanlarını günceller.
#### `account_activation_request`
Aktivasyon e-postası gönderir.
#### `account_activate`
E-posta doğrulama token'ı ile hesabı aktive eder.
#### `account_phone_activation_request`
SMS doğrulama kodu gönderir.
#### `account_activate_phone`
SMS token'ı ile telefonu doğrular.
#### `account_update_subscription`
Abonelik planını değiştirir.
---
### 📍 Nokta Alarmları / Point Alarms
Belirli bir GPS koordinatı ve yarıçap etrafındaki olaylar için uyarı abonelikleri.
#### `point_alarm_register`
Yeni nokta alarmı oluşturur.
| Parametre | Tip | Açıklama |
|-----------|-----|----------|
| `recipientId` | string | Uyarı alıcısı ID |
| `latitude` | number | Merkez enlem |
| `longitude` | number | Merkez boylam |
| `radius` | number | Yarıçap, metre (0 – 50.000) |
| `lightningFilter` | object? | Yıldırım filtresi |
| `thunderstormFilter` | object? | Fırtına filtresi |
| `precipitationFilter` | object? | Yağış filtresi |
| `webhook` | object? | Webhook konfigürasyonu |
#### `point_alarm_update`
Mevcut kaydı günceller.
#### `point_alarm_delete`
Alarm kaydını siler.
#### `point_alarm_get_by_id`
Tekil alarm detayını getirir.
#### `point_alarm_get_by_recipient`
Alıcıya ait tüm alarmları listeler.
#### `point_alarm_list`
Sayfalı alarm listesi. `recipientIds` ile filtreleme yapılabilir.
---
### 🗺️ Coğrafi Alarmlar / Geo Alarms
İdari sınır, poligon veya H3 adresi bazlı uyarı abonelikleri.
#### `geo_alarm_register`
Yeni coğrafi alarm oluşturur. Üç sınır tipi desteklenir:
```json
// İdari sınır
{ "type": "ADMINISTRATIVE", "cityId": 6, "districtId": 60 }
// Poligon
{ "type": "POLYGON", "polygon": { "exterior": [{"lat": 39.9, "lng": 32.8}, ...] } }
// H3 hücre indeksi
{ "type": "H3INDEX", "h3Address": "8f2830828052d25" }
```
#### `geo_alarm_update` / `geo_alarm_delete` / `geo_alarm_get_by_id` / `geo_alarm_get_by_recipient` / `geo_alarm_list`
Nokta alarmlarıyla aynı imza.
#### 🏙️ Konum Kataloğu
| Araç | Açıklama |
|------|----------|
| `geo_alarm_list_cities` | Tüm illeri listeler |
| `geo_alarm_get_city` | `cityId` ile il detayı |
| `geo_alarm_list_districts` | `cityId` ile ilçeleri listeler |
| `geo_alarm_get_district` | `districtId` ile ilçe detayı |
| `geo_alarm_list_neighbourhoods` | `districtId` ile mahalleleri listeler |
| `geo_alarm_get_neighbourhood` | `neighbourhoodId` ile mahalle detayı |
---
### 📅 Tahmin Alarmları / Forecast Alarms
Eşik aşıldığında sabah (04:00 UTC) veya akşam (16:00 UTC) uyarı gönderir.
#### `forecast_alarm_register`
Yeni tahmin alarmı oluşturur.
**Sınır tipleri:**
```json
{ "type": "ADMINISTRATIVE", "cityId": 6, "districtId": 60 }
{ "type": "POINT", "latitude": 39.92, "longitude": 32.85 }
```
**⚠️ Eşik parametreleri:**
| Parametre | Değerler |
|-----------|----------|
| `precipitationThreshold` | mm cinsinden sayısal değer |
| `snowFallThreshold` | `LIGHT` \| `MODERATE` \| `HEAVY` |
| `windGustThreshold` | `STRONG_WIND` \| `STORM` \| `SEVERE_STORM` \| `HURRICANE` |
| `hotTemperatureThreshold` | `HOT_SNAP` \| `HEAVY_HOT_SNAP` \| `EXTREME_HOT_SNAP` |
| `coldTemperatureThreshold` | `COLD_SNAP` \| `HEAVY_COLD_SNAP` \| `EXTREME_COLD_SNAP` |
**📬 Teslimat:**
| Parametre | Açıklama |
|-----------|----------|
| `forecastDays` | 1–7, kaç günlük tahmin |
| `forecastAlarmDelivery` | `MORNING` (04:00 UTC) \| `EVENING` (16:00 UTC) |
#### `forecast_alarm_update` / `forecast_alarm_delete` / `forecast_alarm_get_by_id` / `forecast_alarm_get_by_recipient` / `forecast_alarm_list`
Nokta alarmlarıyla aynı imza.
#### 🏙️ Konum Kataloğu
| Araç | Açıklama |
|------|----------|
| `forecast_alarm_list_cities` | Tüm illeri listeler |
| `forecast_alarm_get_city` | `cityId` ile il detayı |
| `forecast_alarm_list_districts` | `cityId` ile ilçeleri listeler |
| `forecast_alarm_get_district` | `districtId` ile ilçe detayı |
---
## 🏗️ Mimari
```
src/
├── index.ts # MCP server başlatma, tool routing
├── config.ts # Ortam değişkenleri
├── auth.ts # JWT token yönetimi (otomatik yenileme)
├── client.ts # HTTP API istemcisi (HMAC imzalama)
├── security.ts # HMAC-SHA256, nonce, idempotency key
└── tools/
├── lightnings.ts
├── thunderstorms.ts
├── precipitations.ts
├── forecasts.ts
├── auth.ts
├── accounts.ts
├── point-alarms.ts
├── geo-alarms.ts
└── forecast-alarms.ts
```
**İstek akışı:**
```
MCP İstemci
│
▼
index.ts (CallToolRequestSchema)
│
▼
tools/<kategori>.ts ← Zod validasyonu
│
▼
client.ts (apiGet / apiPost / apiPatch / apiDelete)
│ ├── auth.ts → geçerli JWT token al (gerekirse otomatik yenile)
│ └── security.ts → HMAC-SHA256 imzası üret
│
▼
iklim.co REST API
```
---
## 🔐 Kimlik Doğrulama ve Güvenlik
API ile iletişim iki katmanlı güvenlik mekanizması üzerine kuruludur: **JWT tabanlı kimlik doğrulama** ve **HMAC-SHA256 istek imzalama**. Her ikisi de her istekte birlikte kullanılır.
### Dahili Auth Akışı
Server ilk araç çağrısında otomatik olarak login olur; bu işlem dışarıdan tetiklenmez, tamamen içseldir.
```
İlk araç çağrısı
│
▼
getValidAccessToken() ← auth.ts
│
├─ tokenState yok → login()
│ POST /v1/auth/login
│ { username, password }
│ ← { accessToken, refreshToken }
│ JWT payload decode → expiry hesapla
│ tokenState'e kaydet
│
├─ accessToken süresi dolmak üzere (< 30 sn kaldı) → refresh()
│ POST /v1/auth/refresh
│ { refreshToken }
│ ← { accessToken, refreshToken }
│ tokenState güncelle
│
└─ accessToken geçerli → doğrudan döndür
```
> ⚠️ **Önemli:** `login` ve `refresh` endpoint'leri `Authorization: Bearer` header'ı **içermez** — bu istekler yalnızca HMAC imzasıyla doğrulanır (bkz. aşağısı).
### JWT Token Yaşam Döngüsü
Token state bellekte (`tokenState`) tutulur ve her araç çağrısından önce kontrol edilir:
```
tokenState = {
accessToken: string // API isteklerinde kullanılan JWT
refreshToken: string // accessToken yenileme için
accessTokenExpiresAt: number // epoch ms (JWT payload'dan decode edilir)
refreshTokenExpiresAt: number // epoch ms
}
```
Karar ağacı (`EXPIRY_BUFFER_MS = 30.000 ms`):
```
now < accessTokenExpiresAt - 30s → mevcut token'ı kullan
now < refreshTokenExpiresAt - 30s → refresh token ile yenile
aksi hâlde → yeniden login ol
```
30 saniyelik tampon, istek transit süresinde token'ın geçersiz kalması riskini ortadan kaldırır.
### HTTP İstek Header'ları
Her API isteğine (`login` ve `refresh` dahil) aşağıdaki header'lar eklenir:
| Header | Değer | Açıklama |
|--------|-------|----------|
| `Content-Type` | `application/json` | Sabit |
| `Authorization` | `Bearer <accessToken>` | Yalnızca normal API isteklerinde; login/refresh'te **yoktur** |
| `X-Signature` | hex string | HMAC-SHA256 imzası (bkz. aşağısı) |
| `X-Timestamp` | `Date.now()` string | Unix epoch, milisaniye |
| `X-Nonce` | UUID v4 | Her istekte tekil, replay saldırısı önleme |
| `X-Idempotency-Key` | UUID v4 | Yalnızca `POST` ve `PATCH` isteklerinde |
### HMAC-SHA256 İmza Hesabı
`X-Signature` değeri şu dört bileşenin `|` ile birleştirilmesinden elde edilen string'in HMAC-SHA256'sıdır:
```
imzalanacak_veri = "METHOD|PATH_WITH_QUERY|TIMESTAMP|BODY"
X-Signature = HMAC-SHA256(imzalanacak_veri, IKLIM_HMAC_SECRET) → hex
```
**Bileşenler:**
| Bileşen | Açıklama | Örnek |
|---------|----------|-------|
| `METHOD` | HTTP metodu, büyük harf | `POST` |
| `PATH_WITH_QUERY` | Sorgu parametreleri dahil path | `/v1/lightnings/within` veya `/v1/users?page=0` |
| `TIMESTAMP` | `X-Timestamp` ile aynı değer | `1774349677000` |
| `BODY` | JSON body string; body yoksa boş string `""` | `{"username":"..."}` |
**Örnek hesaplama (GET isteği):**
```
METHOD = "GET"
PATH = "/v1/users?pageNumber=0&pageSize=10"
TIMESTAMP = "1774349677000"
BODY = "" ← GET isteğinde body yok
imzalanacak = "GET|/v1/users?pageNumber=0&pageSize=10|1774349677000|"
X-Signature = HMAC-SHA256(imzalanacak, secret) → "a3f9c2..."
```
**Örnek hesaplama (POST isteği):**
```
METHOD = "POST"
PATH = "/v1/lightnings/within"
TIMESTAMP = "1774349677000"
BODY = '{"center":{"lat":39.87,"lng":32.74},"radius":50000,...}'
imzalanacak = "POST|/v1/lightnings/within|1774349677000|{\"center\":...}"
X-Signature = HMAC-SHA256(imzalanacak, secret) → "7be41d..."
```
> Kaynak: [`src/security.ts`](src/security.ts) — `buildSignature()` fonksiyonu
### Auth ile Normal İstekler Arasındaki Fark
| | `POST /v1/auth/login` | `POST /v1/auth/refresh` | Normal API İstekleri |
|---|---|---|---|
| `Authorization` | ❌ | ❌ | ✅ `Bearer <token>` |
| `X-Signature` | ✅ | ✅ | ✅ |
| `X-Timestamp` | ✅ | ✅ | ✅ |
| `X-Nonce` | ✅ | ✅ | ✅ |
| `X-Idempotency-Key` | ✅ | ✅ | ✅ (POST/PATCH) |
### Güvenlik Önerileri
- 🔒 `IKLIM_HMAC_SECRET` ve `IKLIM_PASSWORD` değerlerini kaynak koda veya git geçmişine eklemeyin
- 🏭 Üretim ortamında `.env` dosyası yerine sistem ortam değişkenlerini veya secret manager kullanın
- 🌍 Her ortam (prod/test/local) için ayrı kimlik bilgileri kullanın
- 🔄 HMAC secret'ı düzenli olarak rotate edin
---
## 💻 Geliştirici Notları
**➕ Yeni araç eklemek**
1. `src/tools/` altında ilgili dosyaya yeni tool tanımı ve handler ekle
2. `src/index.ts` içinde `toolHandlerMap`'e ve `allTools` dizisine kaydet
3. `npm run build` ile derle
**✅ Zod şemaları**
Tüm araç girdileri Zod ile çalışma zamanında doğrulanır. Her araç `schema.parse(args)` çağrısından geçer; geçersiz girdi anlamlı bir hata mesajıyla geri döner.
**🔧 TypeScript derleme hedefi**
`tsconfig.json` → `target: ES2022`, `module: Node16`
**📦 Bağımlılıklar**
| Paket | Versiyon | Kullanım |
|-------|----------|----------|
| `@modelcontextprotocol/sdk` | ^1.0.0 | MCP altyapısı |
| `zod` | ^3.23.8 | Girdi validasyonu |
| `typescript` | ^5.5.0 | Derleme |
| `ts-node` | ^10.9.2 | Geliştirme modu |