mackolik-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
@@ -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.
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.5
2
+ Name: mackolik-mcp
3
+ Version: 0.1.0
4
+ Summary: Unofficial read-only MCP server for mackolik.com live scores (live scores, results, fixtures, standings, match detail)
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
+ # mackolik-mcp 🇹🇷
14
+
15
+ mcp-name: io.github.mohammad-abbasi-sh/mackolik-mcp
16
+
17
+ Unofficial **read-only** MCP server for mackolik.com, Türkiye's #1 sports-scores app (~38M installs) — live scores, results, fixtures, league standings and match detail for football (and basketball) worldwide, with Turkish leagues first-class.
18
+
19
+ > Built by Mohammad Abbasi as part of the "Turkey MCP factory" (Sepehr-style pipeline). Not affiliated with mackolik.com. Built for Mackolik only — never Sahadan (same owner, same underlying data).
20
+
21
+ ## For AI agents
22
+
23
+ Add to your MCP client config (stdio):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "mackolik": {
29
+ "command": "uvx",
30
+ "args": ["mackolik-mcp"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ Or run locally: `python -m mackolik_mcp.server` (needs `mcp<2`, `curl-cffi`).
37
+
38
+ ## Tools
39
+
40
+ | Tool | Description |
41
+ |------|-------------|
42
+ | `mackolik_live_scores` | Matches currently in play — `league` optional substring filter (e.g. "Süper Lig", "Premier Lig"), `limit` 1..50; minute, score, half-time, red cards, 1/X/2 odds |
43
+ | `mackolik_results` | Finished matches for one date (default today) — `date` YYYY-MM-DD or DD/MM/YYYY, `league` filter, `limit` 1..50 |
44
+ | `mackolik_fixtures` | Upcoming matches for one date (default today) — same params as results |
45
+ | `mackolik_standings` | League standings — `league`: Tur (Türkiye Süper Lig), Eng (Premier Lig), Esp (LaLiga), Ita (Serie A), Ger (Bundesliga); rank, team, played, points, goal difference |
46
+ | `mackolik_match_detail` | Full detail for one match by `match_id` (from the other tools) + optional `date` (the feed is per-day, so a past match needs its date) |
47
+
48
+ Example output (`mackolik_live_scores` / 2026-11-07):
49
+
50
+ ```json
51
+ {"total_live": 34, "matches": [
52
+ {"match_id": 4542744, "home_team": "Eyüpspor", "away_team": "Kocaelispor",
53
+ "league": "Süper Lig", "country": "Türkiye", "date": "07/11/2026",
54
+ "time": "13:30", "status": "live", "status_text": "17",
55
+ "score": "0-0", "odds_1": "3.46", "odds_x": "2.97", "odds_2": "1.78",
56
+ "url": "https://arsiv.mackolik.com/Futbol-Mac/4542744/eyupspor-kocaelispor"}, …]}
57
+ ```
58
+
59
+ Example output (`mackolik_standings` / Tur):
60
+
61
+ ```json
62
+ {"league": "Tur", "league_name": "Türkiye", "count": 9, "standings": [
63
+ {"rank": 1, "team": "Galatasaray", "played": 10, "points": 25,
64
+ "goal_difference": 11}, …]}
65
+ ```
66
+
67
+ ## How it works
68
+
69
+ mackolik.com redirects to arsiv.mackolik.com (Akamai-fronted, no bot wall). The live data comes from the site's own JSON feed, `vd.mackolik.com/livedata`, which the site's JavaScript calls with `?group=0` (all of today, 836 matches on 2026-11-07) or `?date=DD/MM/YYYY` (1,370 matches for 07/11/2026) — no login, no API key. Match rows are arrays decoded against the site's own `livescore-go.js`: id, teams, status code, status text, score, half-time/red-card detail, kick-off time, 1/X/2 iddaa odds (display only), date, and league group (country, league, season). Status codes: 0/12 = not started, 1/3/5 = live (minute number in status_text — the site adds the `'` when rendering), 14/15/17 = basketball periods, 20 = extra time ("UZ"), 4/13 with "MS" = finished (Maç Sonu). A live/finished match with a blank score field is 0-0 (no goal scored yet). Standings come from the site's own widget handler (`Home/StandingHandler.aspx?league=Tur|Eng|Esp|Ita|Ger`). Transport uses Chrome TLS impersonation (`curl-cffi`, `chrome120`); requests are spaced ~0.8s apart; everything is read-only. Note: "Süper Lig" is also the league name in Greece, Switzerland, Serbia, Slovakia, India and Indonesia — filter by country via the league substring or check the `country` field.
70
+
71
+ ## Install
72
+
73
+ ```bash
74
+ uvx mackolik-mcp # run directly (needs uv)
75
+ # or
76
+ pip install mackolik-mcp
77
+ mackolik-mcp # starts the MCP server on stdio
78
+ ```
79
+
80
+ ## Tests
81
+
82
+ ```bash
83
+ python -m venv .venv && .venv/bin/pip install -e .
84
+ .venv/bin/python -m unittest discover -s tests -v
85
+ ```
86
+
87
+ Offline tests run on recorded fixtures (real feed responses from 2026-11-07); live tests hit mackolik.com. 26/26 green at build time (14 offline + 12 live) plus an MCP stdio smoke test (34 live matches, Süper Lig leader Galatasaray 25 pts).
88
+
89
+ ## Roadmap
90
+
91
+ - Goal scorers / match events (the detail page carries them for finished matches)
92
+ - Basketball live scores as a first-class filter (sport field is already parsed)
93
+ - Head-to-head / team form from the team pages
94
+
95
+ ## License
96
+
97
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,85 @@
1
+ # mackolik-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/mackolik-mcp
4
+
5
+ سرور MCP غیررسمی و **فقط-خواندنی** برای mackolik.com، برنامهٔ شمارهٔ ۱ نتایج زندهٔ ورزشی ترکیه (~۳۸ میلیون نصب) — نتایج زنده، نتایج نهایی، برنامهٔ بازی‌ها، جدول لیگ‌ها و جزئیات مسابقه برای فوتبال (و بسکتبال) در سراسر جهان، با اولویت لیگ‌های ترکیه.
6
+
7
+ > ساخته‌شده توسط محمد عباسی در چارچوب «کارخانهٔ MCP ترکیه» (خط تولید به سبک سپهر). وابسته به mackolik.com نیست. فقط برای Mackolik ساخته شده — هرگز Sahadan (همان مالک، همان دادهٔ زیرین).
8
+
9
+ ## برای ایجنت‌های هوش مصنوعی
10
+
11
+ به پیکربندی MCP client خود اضافه کنید (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "mackolik": {
17
+ "command": "uvx",
18
+ "args": ["mackolik-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ یا به‌صورت محلی اجرا کنید: `python -m mackolik_mcp.server` (نیازمند `mcp<2` و `curl-cffi`).
25
+
26
+ ## ابزارها
27
+
28
+ | ابزار | توضیح |
29
+ |------|-------|
30
+ | `mackolik_live_scores` | بازی‌های در حال برگزاری — فیلتر اختیاری زیررشتهٔ `league` (مثلاً «Süper Lig»، «Premier Lig»)، `limit` ۱ تا ۵۰؛ دقیقه، نتیجه، نیمهٔ اول، کارت‌های قرمز، ضرایب ۱/X/۲ |
31
+ | `mackolik_results` | بازی‌های تمام‌شدهٔ یک تاریخ (پیش‌فرض امروز) — `date` به‌صورت YYYY-MM-DD یا DD/MM/YYYY، فیلتر `league`، `limit` ۱ تا ۵۰ |
32
+ | `mackolik_fixtures` | بازی‌های پیش‌روی یک تاریخ (پیش‌فرض امروز) — همان پارامترهای results |
33
+ | `mackolik_standings` | جدول لیگ — `league`: Tur (سوپر لیگ ترکیه)، Eng (لیگ برتر)، Esp (لالیگا)، Ita (سری A)، Ger (بوندس‌لیگا)؛ رتبه، تیم، بازی، امتیاز، تفاضل گل |
34
+ | `mackolik_match_detail` | جزئیات کامل یک مسابقه با `match_id` (از ابزارهای دیگر) + `date` اختیاری (فید روزانه است، پس برای بازی گذشته تاریخ لازم است) |
35
+
36
+ خروجی نمونه (`mackolik_live_scores` / ۲۰۲۶-۱۱-۰۷):
37
+
38
+ ```json
39
+ {"total_live": 34, "matches": [
40
+ {"match_id": 4542744, "home_team": "Eyüpspor", "away_team": "Kocaelispor",
41
+ "league": "Süper Lig", "country": "Türkiye", "date": "07/11/2026",
42
+ "time": "13:30", "status": "live", "status_text": "17",
43
+ "score": "0-0", "odds_1": "3.46", "odds_x": "2.97", "odds_2": "1.78",
44
+ "url": "https://arsiv.mackolik.com/Futbol-Mac/4542744/eyupspor-kocaelispor"}, …]}
45
+ ```
46
+
47
+ خروجی نمونه (`mackolik_standings` / Tur):
48
+
49
+ ```json
50
+ {"league": "Tur", "league_name": "Türkiye", "count": 9, "standings": [
51
+ {"rank": 1, "team": "Galatasaray", "played": 10, "points": 25,
52
+ "goal_difference": 11}, …]}
53
+ ```
54
+
55
+ ## نحوهٔ کار
56
+
57
+ mackolik.com به arsiv.mackolik.com هدایت می‌شود (جلوِ Akamai، بدون دیوار ربات). دادهٔ زنده از فید JSON خود سایت می‌آید: `vd.mackolik.com/livedata` — جاوااسکریپت سایت آن را با `?group=0` (کل امروز، ۸۳۶ مسابقه در ۲۰۲۶-۱۱-۰۷) یا `?date=DD/MM/YYYY` (۱٬۳۷۰ مسابقه برای ۰۷/۱۱/۲۰۲۶) فرامی‌خواند؛ بدون ورود و کلید API. ردیف‌های مسابقه آرایه‌هایی‌اند که بر اساس فایل `livescore-go.js` خود سایت رمزگشایی شده‌اند: شناسه، تیم‌ها، کد وضعیت، متن وضعیت، نتیجه، جزئیات نیمهٔ اول/کارت قرمز، ساعت شروع، ضرایب ۱/X/۲ (فقط نمایشی)، تاریخ و گروه لیگ (کشور، لیگ، فصل). کدهای وضعیت: ۰/۱۲ = شروع‌نشده، ۱/۳/۵ = زنده (عدد دقیقه در status_text — سایت علامت `'` را هنگام نمایش اضافه می‌کند)، ۱۴/۱۵/۱۷ = وقت‌های بسکتبال، ۲۰ = وقت اضافه («UZ»)، ۴/۱۳ با «MS» = تمام‌شده (Maç Sonu). مسابقهٔ زنده/تمام‌شده با فیلد نتیجهٔ خالی ۰-۰ است (هنوز گلی زده نشده). جدول‌ها از هندلر ویجت خود سایت می‌آیند (`Home/StandingHandler.aspx?league=Tur|Eng|Esp|Ita|Ger`). انتقال با جعل TLS کروم انجام می‌شود (`curl-cffi`، `chrome120`)؛ درخواست‌ها با فاصلهٔ ‎~۰٫۸ ثانیه فرستاده می‌شوند؛ همه‌چیز فقط-خواندنی است. توجه: «Süper Lig» نام لیگ در یونان، سوئیس، صربستان، اسلواکی، هند و اندونزی هم هست — فیلد `country` را بررسی کنید.
58
+
59
+ ## نصب
60
+
61
+ ```bash
62
+ uvx mackolik-mcp # اجرای مستقیم (نیازمند uv)
63
+ # یا
64
+ pip install mackolik-mcp
65
+ mackolik-mcp # سرور MCP را روی stdio راه می‌اندازد
66
+ ```
67
+
68
+ ## تست‌ها
69
+
70
+ ```bash
71
+ python -m venv .venv && .venv/bin/pip install -e .
72
+ .venv/bin/python -m unittest discover -s tests -v
73
+ ```
74
+
75
+ تست‌های آفلاین روی داده‌های ضبط‌شده اجرا می‌شوند (پاسخ‌های واقعی فید از ۲۰۲۶-۱۱-۰۷)؛ تست‌های زنده به mackolik.com وصل می‌شوند. در زمان ساخت ۲۶ از ۲۶ سبز بود (۱۴ آفلاین + ۱۲ زنده) به‌همراه یک تست دود MCP روی stdio (۳۴ مسابقهٔ زنده، صدرنشین سوپر لیگ گالاتاسرای با ۲۵ امتیاز).
76
+
77
+ ## نقشهٔ راه
78
+
79
+ - گل‌زنان / رویدادهای مسابقه (صفحهٔ جزئیات برای بازی‌های تمام‌شده آن‌ها را دارد)
80
+ - نتایج زندهٔ بسکتبال به‌عنوان فیلتر درجه‌یک (فیلد sport همین حالا تجزیه می‌شود)
81
+ - بازی‌های روبه‌رو / فرم تیم از صفحه‌های تیم
82
+
83
+ ## مجوز
84
+
85
+ MIT — ببینید [LICENSE](LICENSE).
@@ -0,0 +1,85 @@
1
+ # mackolik-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/mackolik-mcp
4
+
5
+ mackolik.com için resmi olmayan **salt okunur** MCP sunucusu — Türkiye'nin 1 numaralı canlı skor uygulaması (~38 milyon indirme): dünya genelinde futbol (ve basketbol) için canlı skorlar, sonuçlar, fikstürler, puan durumları ve maç detayları; Türkiye ligleri öncelikli.
6
+
7
+ > Mohammad Abbasi tarafından "Turkey MCP factory" (Sepehr tarzı hat) kapsamında geliştirildi. mackolik.com ile bağlantısı yoktur. Yalnızca Mackolik için geliştirildi — Sahadan asla (aynı sahip, aynı veri).
8
+
9
+ ## Yapay zekâ ajanları için
10
+
11
+ MCP istemci yapılandırmanıza ekleyin (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "mackolik": {
17
+ "command": "uvx",
18
+ "args": ["mackolik-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Veya yerel çalıştırın: `python -m mackolik_mcp.server` (`mcp<2`, `curl-cffi` gerekir).
25
+
26
+ ## Araçlar
27
+
28
+ | Araç | Açıklama |
29
+ |------|----------|
30
+ | `mackolik_live_scores` | Şu an oynanan maçlar — `league` isteğe bağlı alt dize filtresi (örn. "Süper Lig", "Premier Lig"), `limit` 1..50; dakika, skor, ilk yarı, kırmızı kartlar, 1/X/2 oranları |
31
+ | `mackolik_results` | Bir tarihte biten maçlar (varsayılan bugün) — `date` YYYY-MM-DD veya DD/MM/YYYY, `league` filtresi, `limit` 1..50 |
32
+ | `mackolik_fixtures` | Bir tarihte oynanacak maçlar (varsayılan bugün) — results ile aynı parametreler |
33
+ | `mackolik_standings` | Lig puan durumu — `league`: Tur (Türkiye Süper Lig), Eng (Premier Lig), Esp (LaLiga), Ita (Serie A), Ger (Bundesliga); sıra, takım, oynanan, puan, averaj |
34
+ | `mackolik_match_detail` | Bir maçın tüm detayı — `match_id` (diğer araçlardan) + isteğe bağlı `date` (akış günlük olduğu için geçmiş maçın tarihi gerekir) |
35
+
36
+ Örnek çıktı (`mackolik_live_scores` / 2026-11-07):
37
+
38
+ ```json
39
+ {"total_live": 34, "matches": [
40
+ {"match_id": 4542744, "home_team": "Eyüpspor", "away_team": "Kocaelispor",
41
+ "league": "Süper Lig", "country": "Türkiye", "date": "07/11/2026",
42
+ "time": "13:30", "status": "live", "status_text": "17",
43
+ "score": "0-0", "odds_1": "3.46", "odds_x": "2.97", "odds_2": "1.78",
44
+ "url": "https://arsiv.mackolik.com/Futbol-Mac/4542744/eyupspor-kocaelispor"}, …]}
45
+ ```
46
+
47
+ Örnek çıktı (`mackolik_standings` / Tur):
48
+
49
+ ```json
50
+ {"league": "Tur", "league_name": "Türkiye", "count": 9, "standings": [
51
+ {"rank": 1, "team": "Galatasaray", "played": 10, "points": 25,
52
+ "goal_difference": 11}, …]}
53
+ ```
54
+
55
+ ## Nasıl çalışıyor?
56
+
57
+ mackolik.com, arsiv.mackolik.com'a yönlendirir (Akamai ön yüzü, bot duvarı yok). Canlı veriler sitenin kendi JSON akışından gelir: `vd.mackolik.com/livedata` — sitenin JavaScript'i `?group=0` (bugünün tamamı, 2026-11-07'de 836 maç) veya `?date=DD/MM/YYYY` (07/11/2026 için 1.370 maç) ile çağırır; giriş ve API anahtarı gerekmez. Maç satırları, sitenin kendi `livescore-go.js` dosyasına göre çözümlenen dizilerdir: id, takımlar, durum kodu, durum metni, skor, ilk yarı/kırmızı kart detayı, başlama saati, 1/X/2 iddaa oranları (yalnızca bilgi amaçlı), tarih ve lig grubu (ülke, lig, sezon). Durum kodları: 0/12 = başlamadı, 1/3/5 = canlı (dakika sayısı status_text'te — site `'` işaretini render ederken ekler), 14/15/17 = basketbol periyotları, 20 = uzatma ("UZ"), 4/13 ve "MS" = bitti (Maç Sonu). Skoru boş olan canlı/bitmiş maç 0-0'dır (henüz gol yok). Puan durumu sitenin kendi widget işleyicisinden gelir (`Home/StandingHandler.aspx?league=Tur|Eng|Esp|Ita|Ger`). Aktarım Chrome TLS kimliğine bürünme kullanır (`curl-cffi`, `chrome120`); istekler ~0,8 sn arayla yapılır; her şey salt okunurdur. Not: "Süper Lig" aynı zamanda Yunanistan, İsviçre, Sırbistan, Slovakya, Hindistan ve Endonezya'da da lig adıdır — `country` alanını kontrol edin.
58
+
59
+ ## Kurulum
60
+
61
+ ```bash
62
+ uvx mackolik-mcp # doğrudan çalıştır (uv gerekir)
63
+ # veya
64
+ pip install mackolik-mcp
65
+ mackolik-mcp # MCP sunucusunu stdio üzerinde başlatır
66
+ ```
67
+
68
+ ## Testler
69
+
70
+ ```bash
71
+ python -m venv .venv && .venv/bin/pip install -e .
72
+ .venv/bin/python -m unittest discover -s tests -v
73
+ ```
74
+
75
+ Çevrimdışı testler kayıtlı örnek verilerle çalışır (2026-11-07'den gerçek akış yanıtları); canlı testler mackolik.com'a bağlanır. Derleme anında 26/26 yeşil (14 çevrimdışı + 12 canlı) ve bir MCP stdio duman testi (34 canlı maç, Süper Lig lideri Galatasaray 25 puan).
76
+
77
+ ## Yol haritası
78
+
79
+ - Gol atanlar / maç olayları (detay sayfası biten maçlarda bunları taşır)
80
+ - Basketbol canlı skorları birinci sınıf filtre olarak (sport alanı zaten ayrıştırılıyor)
81
+ - Takım sayfalarından karşılıklı maçlar / form durumu
82
+
83
+ ## Lisans
84
+
85
+ MIT — bkz. [LICENSE](LICENSE).
@@ -0,0 +1,85 @@
1
+ # mackolik-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/mackolik-mcp
4
+
5
+ Unofficial **read-only** MCP server for mackolik.com, Türkiye's #1 sports-scores app (~38M installs) — live scores, results, fixtures, league standings and match detail for football (and basketball) worldwide, with Turkish leagues first-class.
6
+
7
+ > Built by Mohammad Abbasi as part of the "Turkey MCP factory" (Sepehr-style pipeline). Not affiliated with mackolik.com. Built for Mackolik only — never Sahadan (same owner, same underlying data).
8
+
9
+ ## For AI agents
10
+
11
+ Add to your MCP client config (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "mackolik": {
17
+ "command": "uvx",
18
+ "args": ["mackolik-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Or run locally: `python -m mackolik_mcp.server` (needs `mcp<2`, `curl-cffi`).
25
+
26
+ ## Tools
27
+
28
+ | Tool | Description |
29
+ |------|-------------|
30
+ | `mackolik_live_scores` | Matches currently in play — `league` optional substring filter (e.g. "Süper Lig", "Premier Lig"), `limit` 1..50; minute, score, half-time, red cards, 1/X/2 odds |
31
+ | `mackolik_results` | Finished matches for one date (default today) — `date` YYYY-MM-DD or DD/MM/YYYY, `league` filter, `limit` 1..50 |
32
+ | `mackolik_fixtures` | Upcoming matches for one date (default today) — same params as results |
33
+ | `mackolik_standings` | League standings — `league`: Tur (Türkiye Süper Lig), Eng (Premier Lig), Esp (LaLiga), Ita (Serie A), Ger (Bundesliga); rank, team, played, points, goal difference |
34
+ | `mackolik_match_detail` | Full detail for one match by `match_id` (from the other tools) + optional `date` (the feed is per-day, so a past match needs its date) |
35
+
36
+ Example output (`mackolik_live_scores` / 2026-11-07):
37
+
38
+ ```json
39
+ {"total_live": 34, "matches": [
40
+ {"match_id": 4542744, "home_team": "Eyüpspor", "away_team": "Kocaelispor",
41
+ "league": "Süper Lig", "country": "Türkiye", "date": "07/11/2026",
42
+ "time": "13:30", "status": "live", "status_text": "17",
43
+ "score": "0-0", "odds_1": "3.46", "odds_x": "2.97", "odds_2": "1.78",
44
+ "url": "https://arsiv.mackolik.com/Futbol-Mac/4542744/eyupspor-kocaelispor"}, …]}
45
+ ```
46
+
47
+ Example output (`mackolik_standings` / Tur):
48
+
49
+ ```json
50
+ {"league": "Tur", "league_name": "Türkiye", "count": 9, "standings": [
51
+ {"rank": 1, "team": "Galatasaray", "played": 10, "points": 25,
52
+ "goal_difference": 11}, …]}
53
+ ```
54
+
55
+ ## How it works
56
+
57
+ mackolik.com redirects to arsiv.mackolik.com (Akamai-fronted, no bot wall). The live data comes from the site's own JSON feed, `vd.mackolik.com/livedata`, which the site's JavaScript calls with `?group=0` (all of today, 836 matches on 2026-11-07) or `?date=DD/MM/YYYY` (1,370 matches for 07/11/2026) — no login, no API key. Match rows are arrays decoded against the site's own `livescore-go.js`: id, teams, status code, status text, score, half-time/red-card detail, kick-off time, 1/X/2 iddaa odds (display only), date, and league group (country, league, season). Status codes: 0/12 = not started, 1/3/5 = live (minute number in status_text — the site adds the `'` when rendering), 14/15/17 = basketball periods, 20 = extra time ("UZ"), 4/13 with "MS" = finished (Maç Sonu). A live/finished match with a blank score field is 0-0 (no goal scored yet). Standings come from the site's own widget handler (`Home/StandingHandler.aspx?league=Tur|Eng|Esp|Ita|Ger`). Transport uses Chrome TLS impersonation (`curl-cffi`, `chrome120`); requests are spaced ~0.8s apart; everything is read-only. Note: "Süper Lig" is also the league name in Greece, Switzerland, Serbia, Slovakia, India and Indonesia — filter by country via the league substring or check the `country` field.
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ uvx mackolik-mcp # run directly (needs uv)
63
+ # or
64
+ pip install mackolik-mcp
65
+ mackolik-mcp # starts the MCP server on stdio
66
+ ```
67
+
68
+ ## Tests
69
+
70
+ ```bash
71
+ python -m venv .venv && .venv/bin/pip install -e .
72
+ .venv/bin/python -m unittest discover -s tests -v
73
+ ```
74
+
75
+ Offline tests run on recorded fixtures (real feed responses from 2026-11-07); live tests hit mackolik.com. 26/26 green at build time (14 offline + 12 live) plus an MCP stdio smoke test (34 live matches, Süper Lig leader Galatasaray 25 pts).
76
+
77
+ ## Roadmap
78
+
79
+ - Goal scorers / match events (the detail page carries them for finished matches)
80
+ - Basketball live scores as a first-class filter (sport field is already parsed)
81
+ - Head-to-head / team form from the team pages
82
+
83
+ ## License
84
+
85
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mackolik-mcp"
7
+ version = "0.1.0"
8
+ description = "Unofficial read-only MCP server for mackolik.com live scores (live scores, results, fixtures, standings, match detail)"
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
+ mackolik-mcp = "mackolik_mcp.server:main"
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["src/mackolik_mcp"]
@@ -0,0 +1 @@
1
+ {"$schema":"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json","description":"Unofficial read-only MCP server for mackolik.com live scores, results, fixtures, standings.","name":"io.github.mohammad-abbasi-sh/mackolik-mcp","packages":[{"identifier":"mackolik-mcp","registryBaseUrl":"https://pypi.org","registryType":"pypi","transport":{"type":"stdio"},"version":"0.1.0"}],"repository":{"source":"github","url":"https://github.com/mohammad-abbasi-sh/mackolik-mcp"},"title":"mackolik.com Live Scores","version":"0.1.0","websiteUrl":"https://github.com/mohammad-abbasi-sh/mackolik-mcp"}
@@ -0,0 +1 @@
1
+ """mackolik_mcp — unofficial read-only MCP server for mackolik.com."""
@@ -0,0 +1,82 @@
1
+ """HTTP layer for mackolik_mcp.
2
+
3
+ mackolik.com redirects to arsiv.mackolik.com (Akamai-fronted, no
4
+ bot wall — plain 200). The live-scores data comes from the site's
5
+ own JSON feed, vd.mackolik.com/livedata, which the site's JS
6
+ (livescore-go.js) calls with ?group=<id> (0 = all) or
7
+ ?date=DD/MM/YYYY — no login, no API key. Standings come from the
8
+ site's own handler, arsiv.mackolik.com/Home/StandingHandler.aspx
9
+ ?league=Tur|Eng|Esp|Ita|Ger (the homepage standings widget calls
10
+ exactly this). The transport uses curl-cffi chrome120
11
+ impersonation, the same proven pattern as the other
12
+ Turkey-factory servers. Requests are kept gentle (one at a time,
13
+ ~0.8s apart) and read-only.
14
+
15
+ Verified live 2026-11-07: livedata?group=0 -> 288KB JSON, 836
16
+ matches for the day; livedata?date=07/11/2026 -> 471KB JSON;
17
+ StandingHandler Tur -> 10-row Süper Lig table.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import time
22
+ from urllib.parse import quote
23
+
24
+ from curl_cffi import requests as _r
25
+
26
+ VD_BASE = "https://vd.mackolik.com/livedata"
27
+ ARSIV_BASE = "https://arsiv.mackolik.com"
28
+ TIMEOUT = 30.0
29
+ MIN_INTERVAL = 0.8 # seconds between requests — be polite
30
+
31
+ _HEADERS = {
32
+ "Accept-Language": "tr-TR,tr;q=0.9,en;q=0.8",
33
+ "Accept": "application/json, text/html, */*",
34
+ "Referer": "https://arsiv.mackolik.com/Canli-Sonuclar",
35
+ }
36
+
37
+ _last_fetch = 0.0
38
+
39
+
40
+ class MackolikError(RuntimeError):
41
+ pass
42
+
43
+
44
+ def _get(url: str) -> str:
45
+ global _last_fetch
46
+ wait = MIN_INTERVAL - (time.monotonic() - _last_fetch)
47
+ if wait > 0:
48
+ time.sleep(wait)
49
+ try:
50
+ resp = _r.get(url, impersonate="chrome120", headers=_HEADERS, timeout=TIMEOUT)
51
+ except Exception as e:
52
+ raise MackolikError(f"network error fetching {url}: {e}") from e
53
+ finally:
54
+ _last_fetch = time.monotonic()
55
+ if resp.status_code == 403:
56
+ raise MackolikError(
57
+ "mackolik.com refused the request (HTTP 403, bot wall) — try again later"
58
+ )
59
+ if resp.status_code == 429:
60
+ raise MackolikError(
61
+ "mackolik.com rate-limited the request (HTTP 429) — try again later"
62
+ )
63
+ if resp.status_code == 404:
64
+ raise MackolikError(f"page not found (HTTP 404): {url}")
65
+ if resp.status_code != 200:
66
+ raise MackolikError(f"unexpected HTTP {resp.status_code} fetching {url}")
67
+ return resp.text
68
+
69
+
70
+ def fetch_livedata_group(group: int = 0) -> str:
71
+ """Live feed for a group (0 = all matches, live + today)."""
72
+ return _get(f"{VD_BASE}?group={group}")
73
+
74
+
75
+ def fetch_livedata_date(date_tr: str) -> str:
76
+ """Live feed for one date, DD/MM/YYYY (the site's own format)."""
77
+ return _get(f"{VD_BASE}?date={quote(date_tr, safe='')}")
78
+
79
+
80
+ def fetch_standings(league: str) -> str:
81
+ """Standings widget HTML for Tur|Eng|Esp|Ita|Ger."""
82
+ return _get(f"{ARSIV_BASE}/Home/StandingHandler.aspx?league={quote(league)}")