n11-mcp 0.1.0__tar.gz

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,20 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write # required for PyPI trusted publishing (OIDC)
12
+ contents: read
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+ - run: pip install build
19
+ - run: python -m build
20
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,61 @@
1
+ name: Publish to MCP Registry
2
+
3
+ # Official MCP Registry (registry.modelcontextprotocol.io) via GitHub OIDC.
4
+ # No secret, no interactive login: the runner's OIDC token proves ownership
5
+ # of the io.github.mohammad-abbasi-sh/* namespace.
6
+ # Refs: https://github.com/modelcontextprotocol/registry
7
+
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ id-token: write # required for OIDC -> MCP Registry
18
+ contents: read
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Sync server.json version to tag
23
+ # Only sync when running on a version tag (v*). On workflow_dispatch
24
+ # (ref = refs/heads/main) keep the version committed in server.json.
25
+ run: |
26
+ VER=""
27
+ if [[ "$GITHUB_REF" == refs/tags/* ]]; then
28
+ TAG="${GITHUB_REF#refs/tags/}"
29
+ VER="${TAG#v}"
30
+ fi
31
+ if [ -n "$VER" ]; then
32
+ python3 - "$VER" << 'EOF'
33
+ import json, sys
34
+ ver = sys.argv[1]
35
+ d = json.load(open('server.json'))
36
+ d['version'] = ver
37
+ for pkg in d.get('packages', []):
38
+ pkg['version'] = ver
39
+ json.dump(d, open('server.json', 'w'), indent=2, ensure_ascii=False)
40
+ print('server.json version ->', ver)
41
+ EOF
42
+ else
43
+ echo "No version tag ($GITHUB_REF); keeping server.json version as-is"
44
+ fi
45
+
46
+ - name: Install mcp-publisher
47
+ run: |
48
+ curl -sL "https://github.com/modelcontextprotocol/registry/releases/download/v1.8.7/mcp-publisher_linux_amd64.tar.gz" -o /tmp/mcp-publisher.tar.gz
49
+ tar -xzf /tmp/mcp-publisher.tar.gz -C /tmp
50
+ sudo mv /tmp/mcp-publisher /usr/local/bin/mcp-publisher
51
+ mcp-publisher --version
52
+
53
+ - name: Validate server.json
54
+ run: mcp-publisher validate server.json
55
+
56
+ - name: Publish to MCP Registry (OIDC)
57
+ run: |
58
+ mcp-publisher login github-oidc
59
+ mcp-publisher publish server.json
60
+ # A registry outage must never break the release; re-run from Actions if needed.
61
+ continue-on-error: true
n11_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohammad Abbasi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
n11_mcp-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.5
2
+ Name: n11-mcp
3
+ Version: 0.1.0
4
+ Summary: Unofficial read-only MCP server for n11.com shopping (search, product details, reviews, categories, price stats)
5
+ Author: Mohammad Abbasi
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: curl-cffi>=0.7
10
+ Requires-Dist: mcp<2
11
+ Description-Content-Type: text/markdown
12
+
13
+ # n11-mcp 🇹🇷
14
+
15
+ mcp-name: io.github.mohammad-abbasi-sh/n11-mcp
16
+
17
+ Unofficial **read-only** MCP server for **n11.com**, Türkiye's #3 marketplace — search millions of products, read product details (price, list price, stock, rating, seller, category), read customer reviews, browse the 11 top categories, and get price statistics (min / median / mean / max) for any query.
18
+
19
+ > Built by Mohammad Abbasi as part of the "Turkey MCP factory" (Sepehr-style pipeline). Not affiliated with n11.com.
20
+
21
+ ## For AI agents
22
+
23
+ Add to your MCP client config (stdio):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "n11": {
29
+ "command": "uvx",
30
+ "args": ["n11-mcp"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ Or run locally: `python -m n11_mcp.server` (needs `mcp<2`, `curl-cffi`).
37
+
38
+ ## Tools
39
+
40
+ | Tool | Description |
41
+ |------|-------------|
42
+ | `n11_search` | Search products by keyword — `query` ("iphone", "kablosuz kulaklık"), `sort` ("relevance" / "price_asc" / "price_desc"), `limit` 1..24, `page` 1-based; returns title, price in TRY, list price, brand, SKU, URL, image, stock status |
43
+ | `n11_product_detail` | Detail of one product (`product` = full URL or path with "/urun/…"): name, brand, price in TRY, list price, stock status, rating, review count, seller (company, store name, authorized-store badge, grade, shipment city), category path, description, images |
44
+ | `n11_list_categories` | The 11 top categories: Turkish name, URL, slug |
45
+ | `n11_price_stats` | Price statistics for a query: count, min / median / mean / max price in TRY across the first page |
46
+ | `n11_product_reviews` | Recent customer reviews of one product: reviewer and star rating (n11 does not server-render review bodies) |
47
+
48
+ Example output (`n11_search` / "iphone"):
49
+
50
+ ```json
51
+ {"query": "iphone", "sort": "relevance", "page": 1, "count": 10, "products": [
52
+ {"title": "Apple iPhone 17 Pro Max 256 GB (Apple Türkiye Garantili)",
53
+ "url": "https://www.n11.com/urun/apple-iphone-17-pro-max-256-gb-apple-turkiye-garantili-100816929",
54
+ "sku": "127340433485", "brand": "Apple", "price_try": 112434.28,
55
+ "original_price_try": 117119.04, "price_currency": "TRY",
56
+ "in_stock": true, "image": "https://n11scdn.akamaized.net/…"}, …]}
57
+ ```
58
+
59
+ Example output (`n11_product_detail` / iPhone 17 Pro Max):
60
+
61
+ ```json
62
+ {"name": "Apple iPhone 17 Pro Max 256 GB (Apple Türkiye Garantili) Turuncu 256 GB Fiyatları ve Özellikleri",
63
+ "sku": "127340433485", "brand": "Apple", "price_try": 112434.28,
64
+ "original_price_try": 117119.04, "price_currency": "TRY", "in_stock": true,
65
+ "rating": 5.0, "review_count": 227,
66
+ "category": "Elektronik > Telefon & Aksesuarları > Cep Telefonu > Apple Cep Telefonu",
67
+ "seller_company": "SALİM TEKEMEN", "seller": "TKMNBILISIM",
68
+ "seller_badge": "Yetkili Mağaza", "seller_grade": 100,
69
+ "shipment_city": "Antalya", "shipment_condition": "Ücretsiz Kargo", …}
70
+ ```
71
+
72
+ Example output (`n11_price_stats` / "iphone"):
73
+
74
+ ```json
75
+ {"query": "iphone", "sort": "relevance", "count": 10, "min_try": 57199.0,
76
+ "median_try": 106099.49, "mean_try": 100880.09, "max_try": 150999.55}
77
+ ```
78
+
79
+ ## How it works
80
+
81
+ n11.com answers plain HTTP clients, and the transport uses Chrome TLS
82
+ impersonation (`curl-cffi`, `chrome120`) so a stricter bot check does not
83
+ break the server. There is no public JSON API — data comes from the
84
+ site's server-rendered pages: search pages carry a schema.org ItemList
85
+ JSON-LD block (20 products per page), product pages carry a schema.org
86
+ Product JSON-LD block plus a breadcrumb category path and an embedded
87
+ seller JSON object, and the homepage carries the 11 top categories.
88
+ Requests are spaced ~0.8s apart; everything is read-only, no login,
89
+ no API key.
90
+
91
+ ## Install
92
+
93
+ ```bash
94
+ uvx n11-mcp # run directly (needs uv)
95
+ # or
96
+ pip install n11-mcp
97
+ n11-mcp # starts the MCP server on stdio
98
+ ```
99
+
100
+ ## Tests
101
+
102
+ ```bash
103
+ python -m venv .venv && .venv/bin/pip install -e .
104
+ .venv/bin/python -m unittest discover -s tests -v
105
+ ```
106
+
107
+ Offline tests run on recorded fixtures (real pages fetched 2026-11-06);
108
+ live tests hit n11.com.
109
+
110
+ ## Roadmap
111
+
112
+ - Category-scoped search (search within one category slug)
113
+ - Other sellers of the same product (compare prices per seller)
114
+ - Price history (track a SKU over days)
115
+
116
+ ## License
117
+
118
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,85 @@
1
+ # n11-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/n11-mcp
4
+
5
+ سرور MCP غیررسمی و **فقط‌خواندنی** برای **n11.com**، مارکت‌پلیس شماره‌ی
6
+ ۳ ترکیه — جست‌وجو در میلیون‌ها کالا، خواندن جزئیات کالا (قیمت، قیمت
7
+ لیست، موجودی، امتیاز، فروشنده، دسته)، خواندن نظرهای مشتریان، مرور ۱۱
8
+ دسته‌ی اصلی، و گرفتن آمار قیمت (کمترین / میانه / میانگین / بیشترین)
9
+ برای هر جست‌وجو.
10
+
11
+ > ساخته‌ی محمد عباسی در چارچوب «کارخانه‌ی MCP ترکیه» (به سبک سپهر). هیچ
12
+ > وابستگی‌ای به n11.com ندارد.
13
+
14
+ ## برای ایجنت‌های هوش مصنوعی
15
+
16
+ به پیکربندی MCP client خود اضافه کنید (stdio):
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "n11": {
22
+ "command": "uvx",
23
+ "args": ["n11-mcp"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ یا به‌صورت محلی اجرا کنید: `python -m n11_mcp.server`
30
+ (نیازمند `mcp<2` و `curl-cffi`).
31
+
32
+ ## ابزارها
33
+
34
+ | ابزار | توضیح |
35
+ |------|-------|
36
+ | `n11_search` | جست‌وجوی کالا با کلیدواژه — `query` (مثل «iphone» یا «kablosuz kulaklık»)، `sort` (‏«relevance» / ‏«price_asc» / ‏«price_desc»)، `limit` از ۱ تا ۲۴، `page` از ۱؛ برمی‌گرداند: عنوان، قیمت به لیر، قیمت لیست، برند، SKU، آدرس، عکس، وضعیت موجودی |
37
+ | `n11_product_detail` | جزئیات یک کالا (`product` = آدرس کامل یا مسیر حاوی «/urun/…»): نام، برند، قیمت به لیر، قیمت لیست، وضعیت موجودی، امتیاز، تعداد نظر، فروشنده (شرکت، نام فروشگاه، نشان فروشگاه مجاز، امتیاز فروشنده، شهر ارسال)، مسیر دسته، توضیح، عکس‌ها |
38
+ | `n11_list_categories` | ۱۱ دسته‌ی اصلی: نام ترکی، آدرس، اسلاگ |
39
+ | `n11_price_stats` | آمار قیمت برای یک جست‌وجو: تعداد، کمترین / میانه / میانگین / بیشترین قیمت (لیر) |
40
+ | `n11_product_reviews` | نظرهای اخیر مشتریان برای یک کالا: نویسنده و امتیاز ستاره‌ای (سایت n11 متن نظرها را سمت سرور رندر نمی‌کند) |
41
+
42
+ نمونه‌ی خروجی (`n11_search` / ‏«iphone»):
43
+
44
+ ```json
45
+ {"query": "iphone", "count": 10, "products": [
46
+ {"title": "Apple iPhone 17 Pro Max 256 GB (Apple Türkiye Garantili)",
47
+ "url": "https://www.n11.com/urun/apple-iphone-17-pro-max-256-gb-apple-turkiye-garantili-100816929",
48
+ "sku": "127340433485", "brand": "Apple", "price_try": 112434.28,
49
+ "original_price_try": 117119.04, "in_stock": true}, …]}
50
+ ```
51
+
52
+ ## چطور کار می‌کند؟
53
+
54
+ سایت n11.com به کلاینت‌های HTTP ساده جواب می‌دهد؛ با این حال لایه‌ی
55
+ انتقال از جعل TLS کروم (`curl-cffi`، ‏`chrome120`) استفاده می‌کند تا
56
+ یک بررسی ربات سخت‌گیرانه‌تر سرور را از کار نیندازد. API عمومی JSON
57
+ ندارد — داده‌ها از HTML رندرشده‌ی سمت سرور می‌آیند: صفحه‌های جست‌وجو
58
+ بلوک ItemList از نوع JSON-LD دارند (۲۰ کالا در هر صفحه)، صفحه‌های کالا
59
+ JSON-LD از نوع Product به‌همراه مسیر دسته (breadcrumb) و آبجکت JSON
60
+ فروشنده، و صفحه‌ی اصلی ۱۱ دسته‌ی اصلی را دارد. درخواست‌ها با فاصله‌ی
61
+ حدود ۰٫۸ ثانیه فرستاده می‌شوند؛ همه‌چیز فقط‌خواندنی است، بدون لاگین و
62
+ بدون کلید API.
63
+
64
+ ## نصب
65
+
66
+ ```bash
67
+ uvx n11-mcp # اجرای مستقیم (نیازمند uv)
68
+ # یا
69
+ pip install n11-mcp
70
+ n11-mcp # اجرای سرور MCP روی stdio
71
+ ```
72
+
73
+ ## تست‌ها
74
+
75
+ ```bash
76
+ python -m venv .venv && .venv/bin/pip install -e .
77
+ .venv/bin/python -m unittest discover -s tests -v
78
+ ```
79
+
80
+ تست‌های آفلاین روی نمونه‌های ضبط‌شده از صفحه‌های واقعی اجرا می‌شوند
81
+ (دریافت‌شده در ۲۰۲۶-۱۱-۰۶)؛ تست‌های زنده به n11.com وصل می‌شوند.
82
+
83
+ ## لایسنس
84
+
85
+ MIT — ببینید [LICENSE](LICENSE).
@@ -0,0 +1,85 @@
1
+ # n11-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/n11-mcp
4
+
5
+ Türkiye'nin 3 numaralı pazaryeri **n11.com** için resmi olmayan,
6
+ **salt okunur** MCP sunucusu — milyonlarca üründe arama yapın, ürün
7
+ detaylarını okuyun (fiyat, liste fiyatı, stok, puan, satıcı, kategori),
8
+ müşteri yorumlarını okuyun, 11 ana kategoriye göz atın ve herhangi bir
9
+ sorgu için fiyat istatistikleri (min / medyan / ortalama / maks) alın.
10
+
11
+ > Mohammad Abbasi tarafından "Turkey MCP factory" (Sepehr tarzı süreç)
12
+ > kapsamında geliştirildi. n11.com ile bağlantısı yoktur.
13
+
14
+ ## Yapay zekâ ajanları için
15
+
16
+ MCP istemci yapılandırmanıza ekleyin (stdio):
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "n11": {
22
+ "command": "uvx",
23
+ "args": ["n11-mcp"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ veya yerel çalıştırın: `python -m n11_mcp.server`
30
+ (`mcp<2`, `curl-cffi` gerekir).
31
+
32
+ ## Araçlar
33
+
34
+ | Araç | Açıklama |
35
+ |------|----------|
36
+ | `n11_search` | Anahtar kelimeyle ürün arama — `query` ("iphone", "kablosuz kulaklık"), `sort` ("relevance" / "price_asc" / "price_desc"), `limit` 1..24, `page` 1'den başlar; başlık, TL fiyat, liste fiyatı, marka, SKU, URL, görsel, stok durumu döndürür |
37
+ | `n11_product_detail` | Bir ürünün detayı (`product` = "/urun/…" içeren URL veya yol): ad, marka, TL fiyat, liste fiyatı, stok durumu, puan, yorum sayısı, satıcı (şirket, mağaza adı, yetkili mağaza rozeti, satıcı puanı, gönderim şehri), kategori yolu, açıklama, görseller |
38
+ | `n11_list_categories` | 11 ana kategori: Türkçe ad, URL, slug |
39
+ | `n11_price_stats` | Bir sorgunun fiyat istatistikleri: adet, min / medyan / ortalama / maks fiyat (TL) |
40
+ | `n11_product_reviews` | Bir ürünün son müşteri yorumları: yorumcu ve yıldız puanı (n11 yorum metinlerini sunucu taraflı işlemez) |
41
+
42
+ Örnek çıktı (`n11_search` / "iphone"):
43
+
44
+ ```json
45
+ {"query": "iphone", "count": 10, "products": [
46
+ {"title": "Apple iPhone 17 Pro Max 256 GB (Apple Türkiye Garantili)",
47
+ "url": "https://www.n11.com/urun/apple-iphone-17-pro-max-256-gb-apple-turkiye-garantili-100816929",
48
+ "sku": "127340433485", "brand": "Apple", "price_try": 112434.28,
49
+ "original_price_try": 117119.04, "in_stock": true}, …]}
50
+ ```
51
+
52
+ ## Nasıl çalışır?
53
+
54
+ n11.com düz HTTP istemcilerine yanıt verir; aktarım yine de Chrome TLS
55
+ taklidi (`curl-cffi`, `chrome120`) kullanır, böylece daha katı bir bot
56
+ kontrolü sunucuyu bozmaz. Herkese açık bir JSON API yoktur — veriler
57
+ sitenin sunucu taraflı işlenmiş HTML sayfalarından gelir: arama
58
+ sayfaları schema.org ItemList JSON-LD bloğu (sayfa başına 20 ürün),
59
+ ürün sayfaları schema.org Product JSON-LD + breadcrumb kategori yolu +
60
+ gömülü satıcı JSON nesnesi, ana sayfa ise 11 ana kategoriyi taşır.
61
+ İstekler ~0.8 sn arayla yapılır; her şey salt okunurdur, giriş
62
+ gerekmez, API anahtarı gerekmez.
63
+
64
+ ## Kurulum
65
+
66
+ ```bash
67
+ uvx n11-mcp # doğrudan çalıştır (uv gerekir)
68
+ # veya
69
+ pip install n11-mcp
70
+ n11-mcp # MCP sunucusunu stdio üzerinde başlatır
71
+ ```
72
+
73
+ ## Testler
74
+
75
+ ```bash
76
+ python -m venv .venv && .venv/bin/pip install -e .
77
+ .venv/bin/python -m unittest discover -s tests -v
78
+ ```
79
+
80
+ Çevrimdışı testler kayıtlı örnek sayfalarla çalışır (gerçek sayfalar,
81
+ 2026-11-06); canlı testler n11.com'a bağlanır.
82
+
83
+ ## Lisans
84
+
85
+ MIT — bkz. [LICENSE](LICENSE).
@@ -0,0 +1,106 @@
1
+ # n11-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/n11-mcp
4
+
5
+ Unofficial **read-only** MCP server for **n11.com**, Türkiye's #3 marketplace — search millions of products, read product details (price, list price, stock, rating, seller, category), read customer reviews, browse the 11 top categories, and get price statistics (min / median / mean / max) for any query.
6
+
7
+ > Built by Mohammad Abbasi as part of the "Turkey MCP factory" (Sepehr-style pipeline). Not affiliated with n11.com.
8
+
9
+ ## For AI agents
10
+
11
+ Add to your MCP client config (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "n11": {
17
+ "command": "uvx",
18
+ "args": ["n11-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Or run locally: `python -m n11_mcp.server` (needs `mcp<2`, `curl-cffi`).
25
+
26
+ ## Tools
27
+
28
+ | Tool | Description |
29
+ |------|-------------|
30
+ | `n11_search` | Search products by keyword — `query` ("iphone", "kablosuz kulaklık"), `sort` ("relevance" / "price_asc" / "price_desc"), `limit` 1..24, `page` 1-based; returns title, price in TRY, list price, brand, SKU, URL, image, stock status |
31
+ | `n11_product_detail` | Detail of one product (`product` = full URL or path with "/urun/…"): name, brand, price in TRY, list price, stock status, rating, review count, seller (company, store name, authorized-store badge, grade, shipment city), category path, description, images |
32
+ | `n11_list_categories` | The 11 top categories: Turkish name, URL, slug |
33
+ | `n11_price_stats` | Price statistics for a query: count, min / median / mean / max price in TRY across the first page |
34
+ | `n11_product_reviews` | Recent customer reviews of one product: reviewer and star rating (n11 does not server-render review bodies) |
35
+
36
+ Example output (`n11_search` / "iphone"):
37
+
38
+ ```json
39
+ {"query": "iphone", "sort": "relevance", "page": 1, "count": 10, "products": [
40
+ {"title": "Apple iPhone 17 Pro Max 256 GB (Apple Türkiye Garantili)",
41
+ "url": "https://www.n11.com/urun/apple-iphone-17-pro-max-256-gb-apple-turkiye-garantili-100816929",
42
+ "sku": "127340433485", "brand": "Apple", "price_try": 112434.28,
43
+ "original_price_try": 117119.04, "price_currency": "TRY",
44
+ "in_stock": true, "image": "https://n11scdn.akamaized.net/…"}, …]}
45
+ ```
46
+
47
+ Example output (`n11_product_detail` / iPhone 17 Pro Max):
48
+
49
+ ```json
50
+ {"name": "Apple iPhone 17 Pro Max 256 GB (Apple Türkiye Garantili) Turuncu 256 GB Fiyatları ve Özellikleri",
51
+ "sku": "127340433485", "brand": "Apple", "price_try": 112434.28,
52
+ "original_price_try": 117119.04, "price_currency": "TRY", "in_stock": true,
53
+ "rating": 5.0, "review_count": 227,
54
+ "category": "Elektronik > Telefon & Aksesuarları > Cep Telefonu > Apple Cep Telefonu",
55
+ "seller_company": "SALİM TEKEMEN", "seller": "TKMNBILISIM",
56
+ "seller_badge": "Yetkili Mağaza", "seller_grade": 100,
57
+ "shipment_city": "Antalya", "shipment_condition": "Ücretsiz Kargo", …}
58
+ ```
59
+
60
+ Example output (`n11_price_stats` / "iphone"):
61
+
62
+ ```json
63
+ {"query": "iphone", "sort": "relevance", "count": 10, "min_try": 57199.0,
64
+ "median_try": 106099.49, "mean_try": 100880.09, "max_try": 150999.55}
65
+ ```
66
+
67
+ ## How it works
68
+
69
+ n11.com answers plain HTTP clients, and the transport uses Chrome TLS
70
+ impersonation (`curl-cffi`, `chrome120`) so a stricter bot check does not
71
+ break the server. There is no public JSON API — data comes from the
72
+ site's server-rendered pages: search pages carry a schema.org ItemList
73
+ JSON-LD block (20 products per page), product pages carry a schema.org
74
+ Product JSON-LD block plus a breadcrumb category path and an embedded
75
+ seller JSON object, and the homepage carries the 11 top categories.
76
+ Requests are spaced ~0.8s apart; everything is read-only, no login,
77
+ no API key.
78
+
79
+ ## Install
80
+
81
+ ```bash
82
+ uvx n11-mcp # run directly (needs uv)
83
+ # or
84
+ pip install n11-mcp
85
+ n11-mcp # starts the MCP server on stdio
86
+ ```
87
+
88
+ ## Tests
89
+
90
+ ```bash
91
+ python -m venv .venv && .venv/bin/pip install -e .
92
+ .venv/bin/python -m unittest discover -s tests -v
93
+ ```
94
+
95
+ Offline tests run on recorded fixtures (real pages fetched 2026-11-06);
96
+ live tests hit n11.com.
97
+
98
+ ## Roadmap
99
+
100
+ - Category-scoped search (search within one category slug)
101
+ - Other sellers of the same product (compare prices per seller)
102
+ - Price history (track a SKU over days)
103
+
104
+ ## License
105
+
106
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "n11-mcp"
7
+ version = "0.1.0"
8
+ description = "Unofficial read-only MCP server for n11.com shopping (search, product details, reviews, categories, price stats)"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "mcp<2",
14
+ "curl-cffi>=0.7",
15
+ ]
16
+ authors = [{ name = "Mohammad Abbasi" }]
17
+
18
+ [project.scripts]
19
+ n11-mcp = "n11_mcp.server:main"
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["src/n11_mcp"]
@@ -0,0 +1 @@
1
+ {"$schema":"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json","description":"Unofficial read-only MCP server for n11.com shopping: search, details, reviews, prices.","name":"io.github.mohammad-abbasi-sh/n11-mcp","packages":[{"identifier":"n11-mcp","registryBaseUrl":"https://pypi.org","registryType":"pypi","transport":{"type":"stdio"},"version":"0.1.0"}],"repository":{"source":"github","url":"https://github.com/mohammad-abbasi-sh/n11-mcp"},"title":"n11.com Shopping","version":"0.1.0","websiteUrl":"https://github.com/mohammad-abbasi-sh/n11-mcp"}
@@ -0,0 +1 @@
1
+ """n11_mcp — unofficial read-only MCP server for n11.com."""
@@ -0,0 +1,88 @@
1
+ """HTTP layer for n11_mcp.
2
+
3
+ n11.com answers HTTP 200 to plain HTTP clients with a browser UA
4
+ (verified live 2026-11-06: homepage, search, product pages). The
5
+ transport still uses curl-cffi chrome120 impersonation, the same proven
6
+ pattern as the other Turkey-factory servers, so a stricter bot check
7
+ does not break the server overnight. Requests are kept gentle (one at
8
+ a time, ~0.8s apart) and read-only.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import time
13
+ from urllib.parse import quote_plus, urlparse
14
+
15
+ from curl_cffi import requests as _r
16
+
17
+ BASE = "https://www.n11.com"
18
+ TIMEOUT = 30.0
19
+ MIN_INTERVAL = 0.8 # seconds between requests — be polite
20
+
21
+ _HEADERS = {
22
+ "Accept-Language": "tr-TR,tr;q=0.9,en;q=0.8",
23
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
24
+ }
25
+
26
+ _last_fetch = 0.0
27
+
28
+
29
+ class N11Error(RuntimeError):
30
+ pass
31
+
32
+
33
+ def _get(url: str) -> str:
34
+ global _last_fetch
35
+ wait = MIN_INTERVAL - (time.monotonic() - _last_fetch)
36
+ if wait > 0:
37
+ time.sleep(wait)
38
+ try:
39
+ resp = _r.get(url, impersonate="chrome120", headers=_HEADERS, timeout=TIMEOUT)
40
+ except Exception as e:
41
+ raise N11Error(f"network error fetching {url}: {e}") from e
42
+ finally:
43
+ _last_fetch = time.monotonic()
44
+ if resp.status_code == 403:
45
+ raise N11Error(
46
+ "n11.com refused the request (HTTP 403, bot wall) — try again later"
47
+ )
48
+ if resp.status_code == 429:
49
+ raise N11Error(
50
+ "n11.com rate-limited the request (HTTP 429) — try again later"
51
+ )
52
+ if resp.status_code == 404:
53
+ raise N11Error(f"page not found (HTTP 404): {url}")
54
+ if resp.status_code != 200:
55
+ raise N11Error(f"unexpected HTTP {resp.status_code} fetching {url}")
56
+ return resp.text
57
+
58
+
59
+ def fetch_search_html(query: str, sort_param: str | None = None, page: int = 1) -> str:
60
+ """Search page HTML. sort_param: 'PRICE_LOW' (price asc) or 'PRICE_HIGH'."""
61
+ url = f"{BASE}/arama?q={quote_plus(query)}"
62
+ if sort_param:
63
+ url += f"&srt={sort_param}"
64
+ if page > 1:
65
+ url += f"&pg={page}"
66
+ return _get(url)
67
+
68
+
69
+ def fetch_product_html(product: str) -> str:
70
+ """Product page HTML. product: full URL or site path '/urun/<slug>-<id>'."""
71
+ path = product.strip()
72
+ if path.startswith("http"):
73
+ parsed = urlparse(path)
74
+ if "n11.com" not in parsed.netloc:
75
+ raise N11Error("product must be an n11.com URL or path")
76
+ path = parsed.path
77
+ if not path.startswith("/"):
78
+ path = "/" + path
79
+ if "/urun/" not in path:
80
+ raise N11Error(
81
+ "product must be a product URL/path containing '/urun/…' "
82
+ "(find one with n11_search first)"
83
+ )
84
+ return _get(f"{BASE}{path}")
85
+
86
+
87
+ def fetch_home_html() -> str:
88
+ return _get(f"{BASE}/")