evimnet-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Location Tools — Şehir, ilçe, mahalle araçları
3
+ */
4
+
5
+ const BASE_URL = process.env.EVIMNET_BASE_URL || 'https://evimnet.com';
6
+
7
+ export const locationTools = [
8
+ {
9
+ name: 'get_locations',
10
+ description: `Türkiye'deki şehir, ilçe ve mahalleleri listele.
11
+ Arama yapmadan önce doğru slug bulmak için kullan.
12
+ Örnek: "Bodrum'un ilçelerini listele", "Antalya'da mahalleler"`,
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ level: {
17
+ type: 'string',
18
+ enum: ['cities', 'districts', 'neighborhoods'],
19
+ description: 'Seviye: cities=Şehirler, districts=İlçeler, neighborhoods=Mahalleler',
20
+ },
21
+ citySlug: {
22
+ type: 'string',
23
+ description: 'İlçe veya mahalle sorgusunda gerekli şehir slug\'ı (örn: bodrum, istanbul)',
24
+ },
25
+ districtSlug: {
26
+ type: 'string',
27
+ description: 'Mahalle sorgusunda gerekli ilçe slug\'ı',
28
+ },
29
+ },
30
+ required: ['level'],
31
+ },
32
+ },
33
+
34
+ {
35
+ name: 'search_location',
36
+ description: `Konum adından slug bul. "Bodrum" → "bodrum", "Beşiktaş" → "besiktas" gibi.
37
+ Arama yapmadan önce doğru şehir/ilçe slug'ını bulmak için kullan.`,
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ query: {
42
+ type: 'string',
43
+ description: 'Aranacak konum adı (Türkçe veya Almanca)',
44
+ },
45
+ },
46
+ required: ['query'],
47
+ },
48
+ },
49
+ ];
50
+
51
+ export async function handleLocationTool(name, args) {
52
+ if (name === 'get_locations') return getLocations(args);
53
+ if (name === 'search_location') return searchLocation(args);
54
+ }
55
+
56
+ async function getLocations(args) {
57
+ const { level, citySlug, districtSlug } = args;
58
+
59
+ if (level === 'cities') {
60
+ const res = await fetch(`${BASE_URL}/api/cities`);
61
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
62
+ const data = await res.json();
63
+
64
+ const cities = data.map(c => ({ name: c.name, slug: c.slug }));
65
+ return {
66
+ content: [{
67
+ type: 'text',
68
+ text: JSON.stringify({ cities, total: cities.length }, null, 2),
69
+ }],
70
+ };
71
+ }
72
+
73
+ if (level === 'districts') {
74
+ if (!citySlug) throw new Error('districts için citySlug gerekli');
75
+
76
+ // Şehir ID'sini bul
77
+ const citiesRes = await fetch(`${BASE_URL}/api/cities`);
78
+ const cities = await citiesRes.json();
79
+ const city = cities.find(c => c.slug === citySlug || c.name.toLowerCase() === citySlug.toLowerCase());
80
+ if (!city) throw new Error(`Şehir bulunamadı: ${citySlug}`);
81
+
82
+ const res = await fetch(`${BASE_URL}/api/districts?cityId=${city.id}`);
83
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
84
+ const data = await res.json();
85
+
86
+ return {
87
+ content: [{
88
+ type: 'text',
89
+ text: JSON.stringify({
90
+ city: { name: city.name, slug: city.slug },
91
+ districts: data.map(d => ({ name: d.name, slug: d.slug })),
92
+ total: data.length,
93
+ }, null, 2),
94
+ }],
95
+ };
96
+ }
97
+
98
+ if (level === 'neighborhoods') {
99
+ if (!citySlug || !districtSlug) throw new Error('neighborhoods için citySlug ve districtSlug gerekli');
100
+
101
+ // İlçe ID'sini bul
102
+ const citiesRes = await fetch(`${BASE_URL}/api/cities`);
103
+ const cities = await citiesRes.json();
104
+ const city = cities.find(c => c.slug === citySlug);
105
+ if (!city) throw new Error(`Şehir bulunamadı: ${citySlug}`);
106
+
107
+ const distRes = await fetch(`${BASE_URL}/api/districts?cityId=${city.id}`);
108
+ const districts = await distRes.json();
109
+ const district = districts.find(d => d.slug === districtSlug || d.name.toLowerCase() === districtSlug.toLowerCase());
110
+ if (!district) throw new Error(`İlçe bulunamadı: ${districtSlug}`);
111
+
112
+ const res = await fetch(`${BASE_URL}/api/neighborhoods?districtId=${district.id}`);
113
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
114
+ const data = await res.json();
115
+
116
+ return {
117
+ content: [{
118
+ type: 'text',
119
+ text: JSON.stringify({
120
+ city: { name: city.name, slug: city.slug },
121
+ district: { name: district.name, slug: district.slug },
122
+ neighborhoods: data.map(n => ({ name: n.name, slug: n.slug })),
123
+ total: data.length,
124
+ }, null, 2),
125
+ }],
126
+ };
127
+ }
128
+
129
+ throw new Error(`Geçersiz seviye: ${level}`);
130
+ }
131
+
132
+ async function searchLocation(args) {
133
+ const { query } = args;
134
+
135
+ const res = await fetch(`${BASE_URL}/api/search?q=${encodeURIComponent(query)}&locale=tr`);
136
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
137
+
138
+ // Şehir listesinden de ara
139
+ const citiesRes = await fetch(`${BASE_URL}/api/cities`);
140
+ const cities = await citiesRes.json();
141
+
142
+ const q = query.toLowerCase().trim();
143
+
144
+ // Normalize Türkçe karakterler
145
+ const normalize = str => str
146
+ .toLowerCase()
147
+ .replace(/ğ/g, 'g').replace(/ü/g, 'u').replace(/ş/g, 's')
148
+ .replace(/ı/g, 'i').replace(/ö/g, 'o').replace(/ç/g, 'c');
149
+
150
+ const nq = normalize(q);
151
+
152
+ const matchedCities = cities
153
+ .filter(c => normalize(c.name).includes(nq))
154
+ .map(c => ({ type: 'city', name: c.name, slug: c.slug }));
155
+
156
+ const matchedDistricts = cities
157
+ .flatMap(c => (c.districts || []).map(d => ({ ...d, cityName: c.name, citySlug: c.slug })))
158
+ .filter(d => normalize(d.name).includes(nq))
159
+ .map(d => ({ type: 'district', name: d.name, slug: d.slug, city: d.cityName, citySlug: d.citySlug }));
160
+
161
+ const results = [...matchedCities, ...matchedDistricts].slice(0, 10);
162
+
163
+ return {
164
+ content: [{
165
+ type: 'text',
166
+ text: JSON.stringify({ query, results, total: results.length }, null, 2),
167
+ }],
168
+ };
169
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Market Tools — Döviz kurları, platform istatistikleri, piyasa bilgisi
3
+ */
4
+
5
+ const BASE_URL = process.env.EVIMNET_BASE_URL || 'https://evimnet.com';
6
+
7
+ export const marketTools = [
8
+ {
9
+ name: 'get_currency_rates',
10
+ description: `Güncel TRY/EUR/USD döviz kurlarını getir (Türkiye Merkez Bankası verileri).
11
+ Fiyatları Avro veya Dolar'a çevirmek için kullan.
12
+ Örnek: "Bu villa kaç Euro?" sorusunda önce bu aracı kullan.`,
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {},
16
+ },
17
+ },
18
+
19
+ {
20
+ name: 'get_platform_stats',
21
+ description: `Evimnet platform istatistiklerini getir: toplam ilan sayısı, emlakçı sayısı, kapsanan şehir sayısı.`,
22
+ inputSchema: {
23
+ type: 'object',
24
+ properties: {},
25
+ },
26
+ },
27
+
28
+ {
29
+ name: 'convert_price',
30
+ description: `TRY fiyatını EUR veya USD'ye çevir. Güncel TCMB kurları kullanılır.
31
+ Örnek: "5.000.000 TL kaç Euro eder?"`,
32
+ inputSchema: {
33
+ type: 'object',
34
+ properties: {
35
+ amountTRY: {
36
+ type: 'number',
37
+ description: 'TRY cinsinden tutar',
38
+ },
39
+ targetCurrency: {
40
+ type: 'string',
41
+ enum: ['EUR', 'USD'],
42
+ description: 'Hedef para birimi',
43
+ },
44
+ },
45
+ required: ['amountTRY', 'targetCurrency'],
46
+ },
47
+ },
48
+ ];
49
+
50
+ export async function handleMarketTool(name, args) {
51
+ if (name === 'get_currency_rates') return getCurrencyRates();
52
+ if (name === 'get_platform_stats') return getPlatformStats();
53
+ if (name === 'convert_price') return convertPrice(args);
54
+ }
55
+
56
+ async function getCurrencyRates() {
57
+ const res = await fetch(`${BASE_URL}/api/currency`);
58
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
59
+ const data = await res.json();
60
+
61
+ return {
62
+ content: [{
63
+ type: 'text',
64
+ text: JSON.stringify({
65
+ date: data.date,
66
+ source: data.source,
67
+ rates: {
68
+ EUR: {
69
+ buying: data.EUR?.buying,
70
+ selling: data.EUR?.selling,
71
+ mid: data.EUR?.mid,
72
+ note: `1 EUR = ${data.EUR?.selling?.toLocaleString('tr-TR')} TRY (satış)`,
73
+ },
74
+ USD: {
75
+ buying: data.USD?.buying,
76
+ selling: data.USD?.selling,
77
+ mid: data.USD?.mid,
78
+ note: `1 USD = ${data.USD?.selling?.toLocaleString('tr-TR')} TRY (satış)`,
79
+ },
80
+ },
81
+ updatedAt: data.updatedAt,
82
+ }, null, 2),
83
+ }],
84
+ };
85
+ }
86
+
87
+ async function getPlatformStats() {
88
+ const res = await fetch(`${BASE_URL}/api/stats`);
89
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
90
+ const data = await res.json();
91
+
92
+ return {
93
+ content: [{
94
+ type: 'text',
95
+ text: JSON.stringify({
96
+ listings: data.listingCount,
97
+ agents: data.agentCount,
98
+ cities: data.cityCount,
99
+ platform: 'Evimnet — Türkiye Gayrimenkul Platformu',
100
+ url: 'https://evimnet.com',
101
+ }, null, 2),
102
+ }],
103
+ };
104
+ }
105
+
106
+ async function convertPrice(args) {
107
+ const { amountTRY, targetCurrency } = args;
108
+
109
+ const res = await fetch(`${BASE_URL}/api/currency`);
110
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
111
+ const data = await res.json();
112
+
113
+ const rate = data[targetCurrency]?.selling;
114
+ if (!rate) throw new Error(`Kur bulunamadı: ${targetCurrency}`);
115
+
116
+ const converted = amountTRY / rate;
117
+ const symbol = targetCurrency === 'EUR' ? '€' : '$';
118
+
119
+ return {
120
+ content: [{
121
+ type: 'text',
122
+ text: JSON.stringify({
123
+ original: `${amountTRY.toLocaleString('tr-TR')} ₺`,
124
+ converted: `${converted.toLocaleString('tr-TR', { maximumFractionDigits: 0 })} ${symbol}`,
125
+ rate: `1 ${targetCurrency} = ${rate.toLocaleString('tr-TR')} ₺`,
126
+ date: data.date,
127
+ source: 'TCMB',
128
+ }, null, 2),
129
+ }],
130
+ };
131
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Nearby Tools — Çevre bilgisi, POI, hava durumu, hava kalitesi
3
+ */
4
+
5
+ const BASE_URL = process.env.EVIMNET_BASE_URL || 'https://evimnet.com';
6
+
7
+ export const nearbyTools = [
8
+ {
9
+ name: 'get_area_info',
10
+ description: `Bir konumun çevresindeki bilgileri getir:
11
+ - Yakın okullar, hastaneler, marketler, restoranlar, parklar, toplu taşıma
12
+ - Anlık hava durumu (sıcaklık, nem, yağış)
13
+ - Hava kalitesi (AQI, PM2.5, PM10)
14
+
15
+ İlan koordinatlarıyla veya şehir/ilçe adıyla kullanılabilir.
16
+ Örnek: "Bu villanın çevresinde ne var?", "Bodrum hava kalitesi nasıl?"`,
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ lat: {
21
+ type: 'number',
22
+ description: 'Enlem (latitude) — ilan detayından alınabilir',
23
+ },
24
+ lng: {
25
+ type: 'number',
26
+ description: 'Boylam (longitude) — ilan detayından alınabilir',
27
+ },
28
+ radius: {
29
+ type: 'number',
30
+ description: 'Arama yarıçapı metre cinsinden (varsayılan: 1500)',
31
+ },
32
+ categories: {
33
+ type: 'array',
34
+ items: {
35
+ type: 'string',
36
+ enum: ['transport', 'school', 'hospital', 'market', 'park', 'restaurant', 'bank', 'worship'],
37
+ },
38
+ description: 'Gösterilecek kategoriler (boş bırakılırsa hepsi)',
39
+ },
40
+ },
41
+ required: ['lat', 'lng'],
42
+ },
43
+ },
44
+ ];
45
+
46
+ export async function handleNearbyTool(name, args) {
47
+ if (name === 'get_area_info') return getAreaInfo(args);
48
+ }
49
+
50
+ async function getAreaInfo(args) {
51
+ const { lat, lng, radius = 1500, categories } = args;
52
+
53
+ const params = new URLSearchParams({
54
+ lat: String(lat),
55
+ lng: String(lng),
56
+ radius: String(radius),
57
+ });
58
+
59
+ const res = await fetch(`${BASE_URL}/api/nearby?${params}`);
60
+ if (!res.ok) throw new Error(`API hatası: ${res.status}`);
61
+ const data = await res.json();
62
+
63
+ // Kategorileri filtrele
64
+ const wantedCats = categories?.length ? categories : null;
65
+
66
+ const poi = {};
67
+ for (const cat of (data.categories || [])) {
68
+ if (wantedCats && !wantedCats.includes(cat.key)) continue;
69
+ if (!cat.items?.length) continue;
70
+
71
+ poi[cat.label] = cat.items.slice(0, 5).map(p => ({
72
+ name: p.name,
73
+ distance: p.distStr || `${p.dist}m`,
74
+ rating: p.rating || null,
75
+ open: p.open != null ? (p.open ? 'Açık' : 'Kapalı') : null,
76
+ }));
77
+ }
78
+
79
+ const weather = data.weather ? {
80
+ temperature: `${data.weather.temp}°C`,
81
+ humidity: `%${data.weather.humidity}`,
82
+ wind: `${data.weather.windSpeed} km/s`,
83
+ precipitation: `%${data.weather.precipProb} yağış ihtimali`,
84
+ } : null;
85
+
86
+ const air = data.airQuality ? {
87
+ aqi: data.airQuality.aqi,
88
+ label: data.airQuality.aqiLabel?.text || null,
89
+ score: data.airQuality.aqiLabel?.score || null,
90
+ pm25: data.airQuality.pm25 ? `${data.airQuality.pm25} µg/m³` : null,
91
+ pm10: data.airQuality.pm10 ? `${data.airQuality.pm10} µg/m³` : null,
92
+ } : null;
93
+
94
+ return {
95
+ content: [{
96
+ type: 'text',
97
+ text: JSON.stringify({
98
+ coordinates: { lat, lng },
99
+ radius: `${radius}m`,
100
+ nearbyPlaces: poi,
101
+ weather,
102
+ airQuality: air,
103
+ cached: data._cached || false,
104
+ }, null, 2),
105
+ }],
106
+ };
107
+ }