# 🌩️ 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= # Zorunlu. İstek imzalama için HMAC-SHA256 anahtarı IKLIM_USERNAME= # Zorunlu. API kullanıcı e-postası IKLIM_PASSWORD= # Zorunlu. API kullanıcı şifresi IKLIM_TOKEN_STORE_PATH=/home/murat/iklim-mcp-server/token-state.bin # Opsiyonel. Access/refresh token binary dosyası IKLIM_HTTP_LOG_PATH= # Opsiyonel. API istek log dosyası (örn: /var/log/iklim-mcp/http.log) IKLIM_HTTP_LOG_MAX_BYTES=5242880 # Opsiyonel. Rotate eşiği (byte), default: 5MB IKLIM_HTTP_LOG_MAX_FILES=5 # Opsiyonel. Tutulacak rotated dosya sayısı (0 = geçmiş dosya tutma) IKLIM_HTTP_LOG_REQUEST_BODY_MAX_BYTES=16384 # Opsiyonel. Request body log limiti (byte) IKLIM_HTTP_LOG_RESPONSE_BODY_MAX_BYTES=16384 # Opsiyonel. Response body log limiti (byte) ``` **🌍 Ortama göre base URL:** | `IKLIM_ENV` | URL | |-------------|-----| | `prod` | `https://api.iklim.co` | | `test` | `https://api-test.iklim.co` | | `local` | `http://localhost:8080` | **📝 API istek logları (rotate):** - `IKLIM_HTTP_LOG_PATH` tanımlıysa MCP server yaptığı tüm API çağrıları için tek satır JSON log yazar. - Her satırda istek header'ları da (`requestHeaders`) bulunur; hassas alanlar maskelenir (`Authorization`, `X-Signature`, `X-Idempotency-Key`, `X-Nonce`). - Request body (`requestBody`) loglanır; JSON ise hassas alanlar maskelenir (örn. `password`, `token`) ve değer `IKLIM_HTTP_LOG_REQUEST_BODY_MAX_BYTES` sınırında kırpılır. - Response header'ları (`responseHeaders`) loglanır; hassas alanlar maskelenir (`Set-Cookie`/`Cookie` dahil). - Response body (`responseBody`) loglanır; JSON ise hassas alanlar maskelenir (`password`, `secret`, `token` vb.) ve değer `IKLIM_HTTP_LOG_RESPONSE_BODY_MAX_BYTES` sınırında kırpılır. - Log dosyası `IKLIM_HTTP_LOG_MAX_BYTES` değerini aşınca rotate olur: - `http.log` → `http.log.1` - eski `http.log.1` → `http.log.2` ... `http.log.` - `IKLIM_HTTP_LOG_MAX_FILES` kadar geçmiş dosya tutulur. --- ## ▶️ 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": "", "IKLIM_USERNAME": "", "IKLIM_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":"","IKLIM_USERNAME":"","IKLIM_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": "", "IKLIM_USERNAME": "", "IKLIM_PASSWORD": "" } } } } ``` ### Diğer MCP İstemcileri MCP stdio standardını destekleyen her istemci kullanılabilir. Gerekli bilgiler: - **transport**: `stdio` - **command**: `node` - **args**: `[""]` - **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ı |
📊 Desteklenen metrikler (53 adet) `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`
#### `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_neighborhoods` | `districtId` ile mahalleleri listeler | | `geo_alarm_get_neighborhood` | `neighborhoodId` 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/.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 ` | 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 | `POST`, `PUT`, `PATCH` ve `DELETE` 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 ` | | `X-Signature` | ✅ | ✅ | ✅ | | `X-Timestamp` | ✅ | ✅ | ✅ | | `X-Nonce` | ✅ | ✅ | ✅ | | `X-Idempotency-Key` | ✅ | ✅ | ✅ (POST/PUT/PATCH/DELETE) | ### 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 |