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.
- mackolik_mcp-0.1.0/.github/workflows/publish.yml +20 -0
- mackolik_mcp-0.1.0/.github/workflows/registry.yml +61 -0
- mackolik_mcp-0.1.0/LICENSE +21 -0
- mackolik_mcp-0.1.0/PKG-INFO +97 -0
- mackolik_mcp-0.1.0/README-fa.md +85 -0
- mackolik_mcp-0.1.0/README-tr.md +85 -0
- mackolik_mcp-0.1.0/README.md +85 -0
- mackolik_mcp-0.1.0/pyproject.toml +22 -0
- mackolik_mcp-0.1.0/server.json +1 -0
- mackolik_mcp-0.1.0/src/mackolik_mcp/__init__.py +1 -0
- mackolik_mcp-0.1.0/src/mackolik_mcp/http.py +82 -0
- mackolik_mcp-0.1.0/src/mackolik_mcp/scores.py +213 -0
- mackolik_mcp-0.1.0/src/mackolik_mcp/server.py +155 -0
- mackolik_mcp-0.1.0/tests/__init__.py +0 -0
- mackolik_mcp-0.1.0/tests/fixtures/livedata-today.json +1 -0
- mackolik_mcp-0.1.0/tests/fixtures/standings-tur.html +151 -0
- mackolik_mcp-0.1.0/tests/test_live.py +126 -0
- mackolik_mcp-0.1.0/tests/test_scores.py +140 -0
|
@@ -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)}")
|