evimnet-mcp 1.0.0 → 1.2.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.
package/DIZIN-KAYIT.md ADDED
@@ -0,0 +1,64 @@
1
+ # MCP Dizin Kayıt Bilgileri
2
+
3
+ Aşağıdaki bilgileri kopyala-yapıştır ile ilgili sitelere gir.
4
+
5
+ ---
6
+
7
+ ## 1. Smithery.ai (en kolay, anında yayınlanır)
8
+
9
+ **URL:** https://smithery.ai/new
10
+
11
+ | Alan | Değer |
12
+ |------|-------|
13
+ | Server Name | evimnet-mcp |
14
+ | Display Name | Evimnet — Turkey Real Estate Search |
15
+ | GitHub URL | https://github.com/high5emlak/evimnetglobal/tree/main/mcp-server |
16
+ | npm Package | evimnet-mcp |
17
+ | Description (EN) | Search properties for sale and rent across Turkey. Covers Istanbul, Bodrum, Antalya, Izmir and all cities. Includes currency conversion (TRY/EUR/USD), area info (schools, hospitals, transport), and market statistics. 9 tools for AI assistants. |
18
+
19
+ ---
20
+
21
+ ## 2. mcp.so
22
+
23
+ **URL:** https://mcp.so/ → Submit butonu
24
+
25
+ | Alan | Değer |
26
+ |------|-------|
27
+ | Name | Evimnet MCP |
28
+ | Description | Turkey's real estate search for AI assistants. Search sale/rent properties, get property details, area info (POIs, weather, AQI), currency rates (TRY/EUR/USD), and platform stats. Supports Turkish, German, and English. |
29
+ | GitHub | https://github.com/high5emlak/evimnetglobal/tree/main/mcp-server |
30
+ | npm | https://www.npmjs.com/package/evimnet-mcp |
31
+ | Install command | npx evimnet-mcp |
32
+ | Category | Real Estate / Property |
33
+ | Tools count | 9 |
34
+ | Tools | search_properties, get_property_details, get_locations, search_location, get_area_info, send_inquiry, get_currency_rates, get_platform_stats, convert_price |
35
+
36
+ ---
37
+
38
+ ## 3. Glama.ai
39
+
40
+ **URL:** https://glama.ai/mcp/servers → Add Server
41
+
42
+ | Alan | Değer |
43
+ |------|-------|
44
+ | GitHub Repository URL | https://github.com/high5emlak/evimnetglobal |
45
+ | Server path | mcp-server/ |
46
+
47
+ ---
48
+
49
+ ## Ortak bilgiler (her yerde kullanılabilir)
50
+
51
+ **Kısa açıklama (TR):**
52
+ Türkiye'de satılık ve kiralık emlak arama. İstanbul, Bodrum, Antalya, İzmir ve tüm şehirler. Döviz çevirimi, çevre bilgisi, piyasa istatistikleri. 9 AI aracı.
53
+
54
+ **Kısa açıklama (EN):**
55
+ Search properties for sale and rent across Turkey. Istanbul, Bodrum, Antalya, Izmir and all cities. Currency conversion, area info, market stats. 9 AI tools.
56
+
57
+ **Kısa açıklama (DE):**
58
+ Immobiliensuche in der Türkei — Kauf und Miete. Istanbul, Bodrum, Antalya, Izmir und alle Städte. Währungsumrechnung, Umgebungsinfos, Marktstatistiken. 9 KI-Tools.
59
+
60
+ **Tags:** real-estate, turkey, property, mcp, ai, bodrum, istanbul, antalya, rental, sale
61
+
62
+ **Website:** https://evimnet.com
63
+ **npm:** https://www.npmjs.com/package/evimnet-mcp
64
+ **License:** MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evimnet-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Evimnet MCP Server — AI asistanlar için Türkiye gayrimenkul araçları",
5
5
  "type": "module",
6
6
  "bin": {
package/server.js CHANGED
@@ -60,10 +60,14 @@ const TOOL_HANDLERS = {
60
60
  convert_price: (a) => handleMarketTool('convert_price', a),
61
61
 
62
62
  // Emlakçı araçları (API key gerekli)
63
- get_my_leads: (a) => handleAgentTool('get_my_leads', a),
64
- update_lead_status: (a) => handleAgentTool('update_lead_status', a),
65
- get_my_listings: (a) => handleAgentTool('get_my_listings', a),
66
- get_my_stats: (a) => handleAgentTool('get_my_stats', a),
63
+ get_my_leads: (a) => handleAgentTool('get_my_leads', a),
64
+ get_lead_detail: (a) => handleAgentTool('get_lead_detail', a),
65
+ update_lead_status: (a) => handleAgentTool('update_lead_status', a),
66
+ get_my_listings: (a) => handleAgentTool('get_my_listings', a),
67
+ get_listing_analytics: (a) => handleAgentTool('get_listing_analytics', a),
68
+ get_my_stats: (a) => handleAgentTool('get_my_stats', a),
69
+ create_listing: (a) => handleAgentTool('create_listing', a),
70
+ update_listing: (a) => handleAgentTool('update_listing', a),
67
71
  };
68
72
 
69
73
  // ─── Server oluştur ──────────────────────────────────────────────────────────
package/tools/agent.js CHANGED
@@ -30,12 +30,23 @@ Filtreleme: durum (NEW=Yeni, CONTACTED=Arandı, MEETING=Görüşme, CLOSED=Kapan
30
30
  enum: ['NEW', 'CONTACTED', 'MEETING', 'CLOSED'],
31
31
  description: 'Durum filtresi (boş = hepsi)',
32
32
  },
33
- page: { type: 'number', description: 'Sayfa numarası (varsayılan: 1)' },
34
- limit: { type: 'number', description: 'Sayfa başı kayıt (maks: 50, varsayılan: 20)' },
35
33
  },
36
34
  },
37
35
  },
38
36
 
37
+ {
38
+ name: 'get_lead_detail',
39
+ description: `Tek bir lead'in detayını getir. İlan bilgisi, mesaj ve iletişim bilgileri dahil.
40
+ Örnek: "Ahmet Yılmaz'ın başvuru detayını göster"`,
41
+ inputSchema: {
42
+ type: 'object',
43
+ properties: {
44
+ id: { type: 'string', description: 'Lead ID (get_my_leads\'ten alınır)' },
45
+ },
46
+ required: ['id'],
47
+ },
48
+ },
49
+
39
50
  {
40
51
  name: 'update_lead_status',
41
52
  description: `Lead durumunu güncelle.
@@ -43,137 +54,217 @@ Filtreleme: durum (NEW=Yeni, CONTACTED=Arandı, MEETING=Görüşme, CLOSED=Kapan
43
54
  inputSchema: {
44
55
  type: 'object',
45
56
  properties: {
46
- leadId: { type: 'string', description: 'Lead ID (get_my_leads\'ten alınır)' },
57
+ id: { type: 'string', description: 'Lead ID' },
47
58
  status: {
48
59
  type: 'string',
49
60
  enum: ['NEW', 'CONTACTED', 'MEETING', 'CLOSED'],
50
61
  description: 'Yeni durum',
51
62
  },
52
63
  },
53
- required: ['leadId', 'status'],
64
+ required: ['id', 'status'],
54
65
  },
55
66
  },
56
67
 
57
68
  {
58
69
  name: 'get_my_listings',
59
- description: `Emlakçının ilanlarını getir.
60
- Aktif, taslak, pasif ilanlar. Lead sayısı ve görüntülenme bilgisi dahil.
70
+ description: `Emlakçının ilanlarını getir. Görüntülenme ve favori sayıları dahil.
61
71
  Örnek: "Aktif ilanlarımı listele", "Taslak ilanlarım var mı?"`,
62
72
  inputSchema: {
63
73
  type: 'object',
64
74
  properties: {
65
75
  status: {
66
76
  type: 'string',
67
- enum: ['DRAFT', 'PENDING', 'APPROVED', 'PASSIVE', 'SOLD', 'RENTED', 'REJECTED'],
77
+ enum: ['DRAFT', 'PENDING', 'APPROVED', 'PASSIVE'],
68
78
  description: 'Durum filtresi (boş = hepsi)',
69
79
  },
70
- page: { type: 'number', description: 'Sayfa numarası' },
71
- limit: { type: 'number', description: 'Sayfa başı kayıt' },
72
80
  },
73
81
  },
74
82
  },
75
83
 
84
+ {
85
+ name: 'get_listing_analytics',
86
+ description: `Bir ilanın performans analitiğini getir: görüntülenme, tıklama, Avrupa görüntülenme, lead sayısı, boost durumu.
87
+ Örnek: "Yalıkavak villamın performansı nasıl?"`,
88
+ inputSchema: {
89
+ type: 'object',
90
+ properties: {
91
+ id: { type: 'string', description: 'İlan ID (get_my_listings\'ten alınır)' },
92
+ },
93
+ required: ['id'],
94
+ },
95
+ },
96
+
76
97
  {
77
98
  name: 'get_my_stats',
78
99
  description: `Dashboard özeti: aktif ilan sayısı, yeni lead sayısı, toplam görüntülenme.
79
- Örnek: "Durumumu özetle", "Bu hafta kaç kişi ilanlarımı gördü?"`,
100
+ Örnek: "Durumumu özetle", "Genel performansım nasıl?"`,
80
101
  inputSchema: {
81
102
  type: 'object',
82
103
  properties: {},
83
104
  },
84
105
  },
106
+
107
+ {
108
+ name: 'create_listing',
109
+ description: `Yeni ilan oluştur. Şehir ve ilçe adını Türkçe yazabilirsiniz, otomatik eşleştirilir.
110
+ Taslak olarak oluşturulur; panelden fotoğraf ekleyip onaya gönderebilirsiniz. status: "PENDING" ile direkt onaya da gönderilebilir.
111
+ Örnek: "Bodrum Yalıkavak'ta 3+1 villa ekle, 4.500.000 TL, 180m², deniz manzaralı"`,
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ title: { type: 'string', description: 'İlan başlığı' },
116
+ type: { type: 'string', enum: ['SALE', 'RENT'], description: 'SALE=Satılık, RENT=Kiralık' },
117
+ propertyType: { type: 'string', enum: ['APARTMENT', 'VILLA', 'LAND', 'PROJECT'], description: 'Mülk tipi' },
118
+ priceTRY: { type: 'number', description: 'Fiyat (TRY)' },
119
+ city: { type: 'string', description: 'Şehir adı (ör: Bodrum, İstanbul, Antalya)' },
120
+ district: { type: 'string', description: 'İlçe adı (ör: Yalıkavak, Beşiktaş)' },
121
+ rooms: { type: 'string', description: 'Oda sayısı (ör: 3+1, 2+1, Stüdyo)' },
122
+ sqmNet: { type: 'number', description: 'Net m²' },
123
+ sqmGross: { type: 'number', description: 'Brüt m²' },
124
+ bathrooms: { type: 'number', description: 'Banyo sayısı' },
125
+ floor: { type: 'number', description: 'Bulunduğu kat' },
126
+ totalFloors: { type: 'number', description: 'Toplam kat' },
127
+ buildingAge: { type: 'number', description: 'Bina yaşı' },
128
+ heating: { type: 'string', description: 'Isıtma tipi (ör: Doğalgaz, Klima, Yerden)' },
129
+ description: { type: 'string', description: 'İlan açıklaması (Türkçe)' },
130
+ descriptionDE: { type: 'string', description: 'İlan açıklaması (Almanca, opsiyonel)' },
131
+ viewType: { type: 'string', description: 'Manzara tipi JSON array (ör: ["SEA","NATURE"])' },
132
+ seaDistance: { type: 'number', description: 'Denize uzaklık (metre)' },
133
+ villaType: { type: 'string', enum: ['DETACHED', 'SEMI_DETACHED', 'TRIPLEX', 'DUPLEX'], description: 'Villa tipi' },
134
+ poolType: { type: 'string', enum: ['PRIVATE', 'SHARED', 'NONE'], description: 'Havuz tipi' },
135
+ gardenSqm: { type: 'number', description: 'Bahçe m²' },
136
+ lat: { type: 'number', description: 'Enlem' },
137
+ lng: { type: 'number', description: 'Boylam' },
138
+ features: { type: 'object', description: 'Özellikler (ör: {"barbeku": true, "klima": true})' },
139
+ status: { type: 'string', enum: ['DRAFT', 'PENDING'], description: 'DRAFT=Taslak, PENDING=Onaya gönder' },
140
+ },
141
+ required: ['title', 'type', 'propertyType', 'priceTRY', 'city'],
142
+ },
143
+ },
144
+
145
+ {
146
+ name: 'update_listing',
147
+ description: `Mevcut bir ilanı düzenle. Sadece değişen alanları gönderin.
148
+ Fiyat, açıklama, m², oda sayısı vb. güncellenebilir. İçerik değişirse ilan otomatik onaya gider.
149
+ Örnek: "Yalıkavak villamın fiyatını 4 milyona düşür", "İlan açıklamasını güncelle"`,
150
+ inputSchema: {
151
+ type: 'object',
152
+ properties: {
153
+ id: { type: 'string', description: 'İlan ID (get_my_listings\'ten alınır)' },
154
+ title: { type: 'string', description: 'Yeni başlık' },
155
+ priceTRY: { type: 'number', description: 'Yeni fiyat (TRY)' },
156
+ description: { type: 'string', description: 'Yeni açıklama' },
157
+ descriptionDE: { type: 'string', description: 'Almanca açıklama' },
158
+ rooms: { type: 'string', description: 'Oda sayısı' },
159
+ sqmNet: { type: 'number', description: 'Net m²' },
160
+ sqmGross: { type: 'number', description: 'Brüt m²' },
161
+ bathrooms: { type: 'number', description: 'Banyo sayısı' },
162
+ floor: { type: 'number', description: 'Kat' },
163
+ buildingAge: { type: 'number', description: 'Bina yaşı' },
164
+ heating: { type: 'string', description: 'Isıtma tipi' },
165
+ viewType: { type: 'string', description: 'Manzara tipi' },
166
+ seaDistance: { type: 'number', description: 'Denize uzaklık' },
167
+ features: { type: 'object', description: 'Özellikler' },
168
+ status: { type: 'string', enum: ['DRAFT', 'PENDING', 'PASSIVE'], description: 'Durum' },
169
+ },
170
+ required: ['id'],
171
+ },
172
+ },
85
173
  ];
86
174
 
87
175
  export async function handleAgentTool(name, args) {
88
- if (name === 'get_my_leads') return getMyLeads(args);
89
- if (name === 'update_lead_status') return updateLeadStatus(args);
90
- if (name === 'get_my_listings') return getMyListings(args);
91
- if (name === 'get_my_stats') return getMyStats();
176
+ if (name === 'get_my_leads') return getMyLeads(args);
177
+ if (name === 'get_lead_detail') return getLeadDetail(args);
178
+ if (name === 'update_lead_status') return updateLeadStatus(args);
179
+ if (name === 'get_my_listings') return getMyListings(args);
180
+ if (name === 'get_listing_analytics') return getListingAnalytics(args);
181
+ if (name === 'get_my_stats') return getMyStats();
182
+ if (name === 'create_listing') return createListing(args);
183
+ if (name === 'update_listing') return updateListing(args);
92
184
  }
93
185
 
94
- async function getMyLeads({ status, page = 1, limit = 20 } = {}) {
95
- const params = new URLSearchParams({ page: String(page), limit: String(limit) });
96
- if (status) params.set('status', status);
97
-
98
- const res = await fetch(`${BASE_URL}/api/mcp/leads?${params}`, { headers: agentHeaders() });
99
- if (!res.ok) throw new Error(`API hatası: ${res.status}`);
100
- const data = await res.json();
101
-
102
- const leads = data.leads.map(l => ({
103
- id: l.id,
104
- name: l.name,
105
- email: l.email,
106
- phone: l.phone || null,
107
- status: l.status,
108
- message: l.message || null,
109
- listing: l.listing ? { title: l.listing.title, slug: l.listing.slug, price: `${l.listing.priceTRY?.toLocaleString('tr-TR')} ₺` } : null,
110
- createdAt: l.createdAt,
111
- }));
186
+ async function mcpPost(action, body) {
187
+ const qs = new URLSearchParams({ action });
188
+ const res = await fetch(`${BASE_URL}/api/mcp/agent?${qs}`, {
189
+ method: 'POST',
190
+ headers: agentHeaders(),
191
+ body: JSON.stringify(body),
192
+ });
193
+ if (!res.ok) {
194
+ const data = await res.json().catch(() => ({}));
195
+ throw new Error(data.error || `API hatasi: ${res.status}`);
196
+ }
197
+ return res.json();
198
+ }
112
199
 
113
- return {
114
- content: [{
115
- type: 'text',
116
- text: JSON.stringify({ leads, total: data.total, page: data.page }, null, 2),
117
- }],
118
- };
200
+ async function mcpGet(action, params = {}) {
201
+ const qs = new URLSearchParams({ action, ...params });
202
+ const res = await fetch(`${BASE_URL}/api/mcp/agent?${qs}`, { headers: agentHeaders() });
203
+ if (!res.ok) {
204
+ const body = await res.json().catch(() => ({}));
205
+ throw new Error(body.error || `API hatası: ${res.status}`);
206
+ }
207
+ return res.json();
119
208
  }
120
209
 
121
- async function updateLeadStatus({ leadId, status }) {
122
- const res = await fetch(`${BASE_URL}/api/mcp/leads/${leadId}`, {
123
- method: 'PATCH',
210
+ async function mcpPatch(action, body) {
211
+ const qs = new URLSearchParams({ action });
212
+ const res = await fetch(`${BASE_URL}/api/mcp/agent?${qs}`, {
213
+ method: 'PATCH',
124
214
  headers: agentHeaders(),
125
- body: JSON.stringify({ status }),
215
+ body: JSON.stringify(body),
126
216
  });
127
- if (!res.ok) throw new Error(`API hatası: ${res.status}`);
217
+ if (!res.ok) {
218
+ const data = await res.json().catch(() => ({}));
219
+ throw new Error(data.error || `API hatası: ${res.status}`);
220
+ }
221
+ return res.json();
222
+ }
128
223
 
129
- return {
130
- content: [{
131
- type: 'text',
132
- text: JSON.stringify({ success: true, leadId, status }, null, 2),
133
- }],
134
- };
224
+ function textResult(data) {
225
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
135
226
  }
136
227
 
137
- async function getMyListings({ status, page = 1, limit = 20 } = {}) {
138
- const params = new URLSearchParams({ page: String(page), limit: String(limit) });
139
- if (status) params.set('status', status);
140
-
141
- const res = await fetch(`${BASE_URL}/api/mcp/listings?${params}`, { headers: agentHeaders() });
142
- if (!res.ok) throw new Error(`API hatası: ${res.status}`);
143
- const data = await res.json();
144
-
145
- const listings = data.listings.map(l => ({
146
- id: l.id,
147
- title: l.title,
148
- slug: l.slug,
149
- type: l.type,
150
- status: l.status,
151
- price: `${l.priceTRY?.toLocaleString('tr-TR')} ₺`,
152
- sqm: l.sqmNet ? `${l.sqmNet}m²` : null,
153
- rooms: l.rooms || null,
154
- city: l.city?.name || null,
155
- district: l.district?.name || null,
156
- leads: l._count?.leads || 0,
157
- url: `https://evimnet.com/ilan/${l.slug}`,
158
- }));
228
+ async function getMyLeads({ status } = {}) {
229
+ const params = {};
230
+ if (status) params.status = status;
231
+ const data = await mcpGet('leads', params);
232
+ return textResult(data);
233
+ }
159
234
 
160
- return {
161
- content: [{
162
- type: 'text',
163
- text: JSON.stringify({ listings, total: data.total }, null, 2),
164
- }],
165
- };
235
+ async function getLeadDetail({ id }) {
236
+ const data = await mcpGet('lead', { id });
237
+ return textResult(data);
238
+ }
239
+
240
+ async function updateLeadStatus({ id, status }) {
241
+ const data = await mcpPatch('lead-status', { id, status });
242
+ return textResult(data);
243
+ }
244
+
245
+ async function getMyListings({ status } = {}) {
246
+ const params = {};
247
+ if (status) params.status = status;
248
+ const data = await mcpGet('listings', params);
249
+ return textResult(data);
250
+ }
251
+
252
+ async function getListingAnalytics({ id }) {
253
+ const data = await mcpGet('analytics', { id });
254
+ return textResult(data);
166
255
  }
167
256
 
168
257
  async function getMyStats() {
169
- const res = await fetch(`${BASE_URL}/api/mcp/stats`, { headers: agentHeaders() });
170
- if (!res.ok) throw new Error(`API hatası: ${res.status}`);
171
- const data = await res.json();
258
+ const data = await mcpGet('stats');
259
+ return textResult(data);
260
+ }
172
261
 
173
- return {
174
- content: [{
175
- type: 'text',
176
- text: JSON.stringify(data, null, 2),
177
- }],
178
- };
262
+ async function createListing(args) {
263
+ const data = await mcpPost('create-listing', args);
264
+ return textResult(data);
265
+ }
266
+
267
+ async function updateListing(args) {
268
+ const data = await mcpPatch('update-listing', args);
269
+ return textResult(data);
179
270
  }