tff-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
tff_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.
tff_mcp-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.5
2
+ Name: tff-mcp
3
+ Version: 0.1.0
4
+ Summary: Unofficial read-only MCP server for tff.org (Turkish Football Federation): standings, fixtures, match details, scorers
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
+ # tff-mcp 🇹🇷
14
+
15
+ mcp-name: io.github.mohammad-abbasi-sh/tff-mcp
16
+
17
+ Unofficial **read-only** MCP server for tff.org, the Turkish Football Federation (TFF) — league standings (puan cetveli), fixtures and results (fikstür), full match details, and top scorers (gol krallığı) for 18 Turkish leagues, from the Süper Lig down to the youth development leagues.
18
+
19
+ > Built by Mohammad Abbasi as part of the "Turkey MCP factory" (Sepehr-style pipeline). Not affiliated with tff.org or the TFF.
20
+
21
+ ## For AI agents
22
+
23
+ Add to your MCP client config (stdio):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "tff": {
29
+ "command": "uvx",
30
+ "args": ["tff-mcp"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ Or run locally: `python -m tff_mcp.server` (needs `mcp<2`, `curl-cffi`).
37
+
38
+ ## Tools
39
+
40
+ | Tool | Description |
41
+ |------|-------------|
42
+ | `tff_list_leagues` | The 18 covered leagues (slug, Turkish name, TFF page id, multi-group flag, top-scorers availability) |
43
+ | `tff_standings` | League standings — `league` slug (default "trendyol-super-lig"); `group` for multi-group leagues: index ("0"), group id, or name ("Kırmızı", "Beyaz", "01"); rank, team, played/won/drawn/lost, goals for/against, goal difference, points |
44
+ | `tff_fixtures` | Fixtures and results — `league` slug; `week` to filter to one match-week; `group` for multi-group leagues; home/away, score (null when not yet played), status ("played"/"scheduled"), TFF match id and match-detail URL |
45
+ | `tff_match_detail` | Full detail of one match — `mac_id` (as returned by `tff_fixtures`, e.g. "317874"); teams and score, date/time, stadium, league/season, match code, the officiating team (referee, assistants, VAR/AVAR, observer, representative), goals with minutes, both starting XIs |
46
+ | `tff_top_scorers` | Top scorers — `league`: "trendyol-super-lig" (default), "trendyol-1-lig", "nesine-2-lig" or "nesine-3-lig" (the four leagues with a TFF scorers page); `limit` 1..50 |
47
+
48
+ Example output (`tff_match_detail` / Gaziantep FK 0–2 Kasımpaşa, 6 Nov 2026):
49
+
50
+ ```json
51
+ {"home": "GAZİANTEP FUTBOL KULÜBÜ A.Ş.", "away": "KASIMPAŞA A.Ş.",
52
+ "score_home": 0, "score_away": 2, "date": "6.11.2026", "time": "20:00",
53
+ "stadium": "GAZİANTEP STADYUMU - GAZİANTEP - ŞEHİTKAMİL",
54
+ "league": "Trendyol Süper Lig Adnan Süvari Sezonu (Profesyonel Takım)",
55
+ "match_code": "1091",
56
+ "officials": [{"name": "ÜMİT ÖZTÜRK", "role": "Hakem"},
57
+ {"name": "ERKAN ENGİN", "role": "VAR"}],
58
+ "goals": [{"player": "HARIS HAJRADINOVIC", "minute": 3, "type": "F"},
59
+ {"player": "ADRIAN DAWID BENEDYCZAK", "minute": 21, "type": "P"}]}
60
+ ```
61
+
62
+ ## How it works
63
+
64
+ tff.org serves its league pages fully server-rendered — no login, no API key, no session cookies. Standings live in `<table class="s-table">` (header O G B M A Y AV P); fixtures in `<table class="fiksturListesiTable">` ("N.Hafta" headers, match rows with home club link, score link `pageID=29&macId=...`, away club link — unplayed matches show " - "). Multi-group leagues (Nesine 2. Lig, Nesine 3. Lig, BAL, women's and youth leagues) expose their groups as `?pageID=<id>&grupID=<n>` links. Match details carry the teams in `lnkTakim1`/`lnkTakim2`, scores in `MacDetaySayi` spans, league/season in `lblOrganizasyonAdi`, match code in `lblKod`, date/time in `lblTarih`, the stadium as text between the code and the date, officials as `NAME(ROLE)` pairs, goals as "NAME,3.dk (F)" entries, and the starting XIs in the `grdTakim1/2_rptKadrolar` repeaters. Top scorers come from the `rptGolKralligi` repeater (pages 821/822/823/824). The site is legacy ASP.NET served as windows-1254, decoded explicitly. One site feature is documented, not faked: the weekly referees page (`pageID=600`) lists the 2./3. Lig rounds with EMPTY referee columns until appointments are announced close to match day — so referee data comes from the match-detail page instead. Transport uses Chrome TLS impersonation (`curl-cffi`, `chrome120`); requests are spaced ~0.8s apart; everything is read-only.
65
+
66
+ ## Install
67
+
68
+ ```bash
69
+ uvx tff-mcp # run directly (needs uv)
70
+ # or
71
+ pip install tff-mcp
72
+ tff-mcp # starts the MCP server on stdio
73
+ ```
74
+
75
+ ## Tests
76
+
77
+ ```bash
78
+ python -m venv .venv && .venv/bin/pip install -e .
79
+ .venv/bin/python -m unittest discover -s tests -v
80
+ ```
81
+
82
+ Offline tests run on recorded fixtures (real pages fetched 2026-11-09); live tests hit tff.org. 37/37 green at build time (20 offline + 17 live) plus an MCP stdio smoke test.
83
+
84
+ ## Roadmap
85
+
86
+ - Disciplinary decisions (PFDK/AFDK pages) and suspended players (pageID=633)
87
+ - Club pages (pageID=28&kulupID=...) — squad lists
88
+ - National-team matches (pageID=425)
89
+
90
+ ## License
91
+
92
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,80 @@
1
+ # tff-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/tff-mcp
4
+
5
+ سرور MCP غیررسمی و **فقط‌خواندنی** برای tff.org (فدراسیون فوتبال ترکیه — TFF) — جدول رده‌بندی (پوان جتورلی)، برنامه و نتایج بازی‌ها (فیکستور)، جزئیات کامل هر مسابقه و جدول گلزنان برتر (گل کرال‌لیغی) برای ۱۸ لیگ ترکیه، از سوپرلیگ تا لیگ‌های جوانان.
6
+
7
+ > ساختهٔ محمد عباسی در چارچوب «کارخانهٔ MCP ترکیه» (خط لوله به سبک سپهر). وابسته به tff.org یا TFF نیست.
8
+
9
+ ## برای ایجنت‌های هوش مصنوعی
10
+
11
+ به پیکربندی کلاینت MCP خود اضافه کنید (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "tff": {
17
+ "command": "uvx",
18
+ "args": ["tff-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ یا به‌صورت محلی اجرا کنید: `python -m tff_mcp.server` (نیازمند `mcp<2` و `curl-cffi`).
25
+
26
+ ## ابزارها
27
+
28
+ | ابزار | توضیح |
29
+ |------|--------|
30
+ | `tff_list_leagues` | ۱۸ لیگ تحت پوشش (slug، نام ترکی، شناسهٔ صفحهٔ TFF، چندگروهی بودن، وجود صفحهٔ گلزنان) |
31
+ | `tff_standings` | جدول رده‌بندی — `league` ‏(پیش‌فرض ‏«trendyol-super-lig»)؛ ‏`group` برای لیگ‌های چندگروهی: شمارهٔ گروه (‏«0»)، شناسهٔ گروه یا نام آن (‏«Kırmızı»، ‏«Beyaz»، ‏«01»)؛ رتبه، تیم، بازی/برد/مساوی/باخت، گل زده/خورده، تفاضل گل، امتیاز |
32
+ | `tff_fixtures` | برنامه و نتایج — `league`؛ ‏`week` برای فیلتر یک هفته؛ ‏`group` برای لیگ‌های چندگروهی؛ میزبان/میهمان، نتیجه (اگر بازی نشده باشد null)، وضعیت (‏«played»/«scheduled»)، شناسهٔ مسابقه در TFF و نشانی جزئیات مسابقه |
33
+ | `tff_match_detail` | جزئیات کامل یک مسابقه — `mac_id` (همان که `tff_fixtures` برمی‌گرداند، مثلاً ‏«317874»)؛ تیم‌ها و نتیجه، تاریخ و ساعت، ورزشگاه، لیگ/فصل، کد مسابقه، تیم داوری (داور، کمک‌داورها، VAR/AVAR، ناظر، نماینده)، گل‌ها با دقیقه، ترکیب اصلی هر دو تیم |
34
+ | `tff_top_scorers` | گلزنان برتر — `league`: ‏«trendyol-super-lig» (پیش‌فرض)، ‏«trendyol-1-lig»، ‏«nesine-2-lig» یا ‏«nesine-3-lig» (چهار لیگی که صفحهٔ گلزنان دارند)؛ ‏`limit` ‏۱ تا ۵۰ |
35
+
36
+ نمونهٔ خروجی (`tff_match_detail` / غازی‌انتپ ۰–۲ قاسم‌پاشا، ۶ نوامبر ۲۰۲۶):
37
+
38
+ ```json
39
+ {"home": "GAZİANTEP FUTBOL KULÜBÜ A.Ş.", "away": "KASIMPAŞA A.Ş.",
40
+ "score_home": 0, "score_away": 2, "date": "6.11.2026", "time": "20:00",
41
+ "stadium": "GAZİANTEP STADYUMU - GAZİANTEP - ŞEHİTKAMİL",
42
+ "league": "Trendyol Süper Lig Adnan Süvari Sezonu (Profesyonel Takım)",
43
+ "match_code": "1091",
44
+ "officials": [{"name": "ÜMİT ÖZTÜRK", "role": "Hakem"},
45
+ {"name": "ERKAN ENGİN", "role": "VAR"}],
46
+ "goals": [{"player": "HARIS HAJRADINOVIC", "minute": 3, "type": "F"},
47
+ {"player": "ADRIAN DAWID BENEDYCZAK", "minute": 21, "type": "P"}]}
48
+ ```
49
+
50
+ ## چگونه کار می‌کند
51
+
52
+ tff.org صفحات لیگ را کاملاً سمت سرور رندر می‌کند — بدون ورود، کلید API و کوکی. جدول رده‌بندی در `<table class="s-table">` است (سربرگ O G B M A Y AV P)؛ فیکستور در `<table class="fiksturListesiTable">` (سطرهای سربرگ «N.Hafta» و سطرهای مسابقه با پیوند باشگاه میزبان، پیوند نتیجه `pageID=29&macId=...` و پیوند باشگاه میهمان — بازی‌های انجام‌نشده « - » نشان می‌دهند). لیگ‌های چندگروهی (نسینه ۲. لیگ، نسینه ۳. لیگ، BAL، لیگ‌های بانوان و جوانان) گروه‌هایشان را با پیوندهای `?pageID=<id>&grupID=<n>` نشان می‌دهند. در جزئیات مسابقه، تیم‌ها در `lnkTakim1`/`lnkTakim2`، نتیجه‌ها در spanهای `MacDetaySayi`، لیگ/فصل در `lblOrganizasyonAdi`، کد مسابقه در `lblKod`، تاریخ/ساعت در `lblTarih`، ورزشگاه در متن میان کد و تاریخ، داوران به‌صورت جفت‌های `NAME(ROLE)`، گل‌ها به شکل «NAME,3.dk (F)» و ترکیب‌های اصلی در تکرارکننده‌های `grdTakim1/2_rptKadrolar` هستند. گلزنان برتر از تکرارکنندهٔ `rptGolKralligi` می‌آیند (صفحات ۸۲۱/۸۲۲/۸۲۳/۸۲۴). سایت یک ASP.NET قدیمی با کدگذاری windows-1254 است و صریحاً دیکد می‌شود. یک قابلیت سایت جعل نمی‌شود، بلکه مستند شده است: صفحهٔ داوران هفته (`pageID=600`) هفته‌های لیگ ۲ و ۳ را با ستون‌های داوری خالی نشان می‌دهد — انتصاب‌ها نزدیک روز مسابقه اعلام می‌شوند — پس دادهٔ داوری از صفحهٔ جزئیات مسابقه گرفته می‌شود. انتقال با جعل TLS کروم است (`curl-cffi`، ‏`chrome120`)؛ درخواست‌ها با فاصلهٔ ~۰٫۸ ثانیه؛ همه‌چیز فقط‌خواندنی است.
53
+
54
+ ## نصب
55
+
56
+ ```bash
57
+ uvx tff-mcp # اجرای مستقیم (نیازمند uv)
58
+ # یا
59
+ pip install tff-mcp
60
+ tff-mcp # سرور MCP را روی stdio راه می‌اندازد
61
+ ```
62
+
63
+ ## تست‌ها
64
+
65
+ ```bash
66
+ python -m venv .venv && .venv/bin/pip install -e .
67
+ .venv/bin/python -m unittest discover -s tests -v
68
+ ```
69
+
70
+ تست‌های آفلاین روی فیکسچرهای ضبط‌شده اجرا می‌شوند (صفحات واقعی در ۲۰۲۶-۱۱-۰۹ گرفته شده‌اند)؛ تست‌های زنده به tff.org می‌روند. در زمان ساخت ۳۷/۳۷ سبز (۲۰ آفلاین + ۱۷ زنده) به‌علاوهٔ یک تست دودی MCP روی stdio.
71
+
72
+ ## نقشهٔ راه
73
+
74
+ - آرای انضباطی (صفحات PFDK/AFDK) و بازیکنان محروم (pageID=633)
75
+ - صفحات باشگاه‌ها (pageID=28&kulupID=...) — فهرست بازیکنان
76
+ - بازی‌های تیم ملی (pageID=425)
77
+
78
+ ## مجوز
79
+
80
+ MIT — ببینید [LICENSE](LICENSE).
@@ -0,0 +1,80 @@
1
+ # tff-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/tff-mcp
4
+
5
+ tff.org (Türkiye Futbol Federasyonu — TFF) için resmi olmayan **salt okunur** MCP sunucusu — Süper Lig'den gençlik gelişim liglerine kadar 18 Türkiye liginde puan cetveli, fikstür ve sonuçlar, maç detayları ve gol krallığı.
6
+
7
+ > Mohammad Abbasi tarafından "Turkey MCP factory" (Sepehr tarzı hat) kapsamında geliştirildi. tff.org veya TFF ile bağlantısı yoktur.
8
+
9
+ ## Yapay zekâ ajanları için
10
+
11
+ MCP istemci yapılandırmanıza ekleyin (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "tff": {
17
+ "command": "uvx",
18
+ "args": ["tff-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Veya yerelde çalıştırın: `python -m tff_mcp.server` (`mcp<2`, `curl-cffi` gerekir).
25
+
26
+ ## Araçlar
27
+
28
+ | Araç | Açıklama |
29
+ |------|-------------|
30
+ | `tff_list_leagues` | Kapsanan 18 lig (slug, Türkçe ad, TFF sayfa kimliği, çok gruplu bilgisi, gol krallığı sayfası var mı) |
31
+ | `tff_standings` | Puan cetveli — `league` slug (varsayılan "trendyol-super-lig"); çok gruplu liglerde `group`: sıra ("0"), grup kimliği veya ad ("Kırmızı", "Beyaz", "01"); sıra, takım, oynanan/galibiyet/beraberlik/mağlubiyet, atılan/yenilen gol, averaj, puan |
32
+ | `tff_fixtures` | Fikstür ve sonuçlar — `league` slug; `week` ile tek haftaya filtre; çok gruplu liglerde `group`; ev sahibi/deplasman, skor (oynanmadıysa null), durum ("played"/"scheduled"), TFF maç kimliği ve maç detay URL'si |
33
+ | `tff_match_detail` | Bir maçın tam detayı — `mac_id` (`tff_fixtures`'ın döndürdüğü, örn. "317874"); takımlar ve skor, tarih/saat, stadyum, lig/sezon, maç kodu, hakem ekibi (hakem, yardımcılar, VAR/AVAR, gözlemci, temsilci), dakikalarıyla goller, iki takımın ilk 11'i |
34
+ | `tff_top_scorers` | Gol krallığı — `league`: "trendyol-super-lig" (varsayılan), "trendyol-1-lig", "nesine-2-lig" veya "nesine-3-lig" (gol krallığı sayfası olan dört lig); `limit` 1..50 |
35
+
36
+ Örnek çıktı (`tff_match_detail` / Gaziantep FK 0–2 Kasımpaşa, 6 Kasım 2026):
37
+
38
+ ```json
39
+ {"home": "GAZİANTEP FUTBOL KULÜBÜ A.Ş.", "away": "KASIMPAŞA A.Ş.",
40
+ "score_home": 0, "score_away": 2, "date": "6.11.2026", "time": "20:00",
41
+ "stadium": "GAZİANTEP STADYUMU - GAZİANTEP - ŞEHİTKAMİL",
42
+ "league": "Trendyol Süper Lig Adnan Süvari Sezonu (Profesyonel Takım)",
43
+ "match_code": "1091",
44
+ "officials": [{"name": "ÜMİT ÖZTÜRK", "role": "Hakem"},
45
+ {"name": "ERKAN ENGİN", "role": "VAR"}],
46
+ "goals": [{"player": "HARIS HAJRADINOVIC", "minute": 3, "type": "F"},
47
+ {"player": "ADRIAN DAWID BENEDYCZAK", "minute": 21, "type": "P"}]}
48
+ ```
49
+
50
+ ## Nasıl çalışır
51
+
52
+ tff.org lig sayfalarını tamamen sunucu taraflı gösterir — giriş, API anahtarı ve çerez gerekmez. Puan cetveli `<table class="s-table">` içindedir (başlık O G B M A Y AV P); fikstür `<table class="fiksturListesiTable">` içindedir ("N.Hafta" başlıkları, ev sahibi kulüp bağlantısı, skor bağlantısı `pageID=29&macId=...` ve deplasman kulüp bağlantısı olan maç satırları — oynanmamış maçlarda " - " yazar). Çok gruplu ligler (Nesine 2. Lig, Nesine 3. Lig, BAL, kadın ve gençlik ligleri) gruplarını `?pageID=<id>&grupID=<n>` bağlantılarıyla gösterir. Maç detaylarında takımlar `lnkTakim1`/`lnkTakim2` içinde, skorlar `MacDetaySayi` span'lerinde, lig/sezon `lblOrganizasyonAdi` içinde, maç kodu `lblKod` içinde, tarih/saat `lblTarih` içinde, stadyum kod ile tarih arasındaki metinde, hakemler `İSİM(ROL)` çiftleri olarak, goller "İSİM,3.dk (F)" biçiminde, ilk 11'ler `grdTakim1/2_rptKadrolar` tekrarlayıcılarındadır. Gol krallığı `rptGolKralligi` tekrarlayıcısından gelir (sayfalar 821/822/823/824). Site windows-1254 kodlamalı eski bir ASP.NET sitesidir, açıkça çözülür. Bir site özelliği uydurulmaz, belgelenir: haftanın hakemleri sayfası (`pageID=600`) 2./3. Lig haftalarını hakem sütunları BOŞ gösterir — atamalar maça yakın açıklanır — bu yüzden hakem verisi maç detay sayfasından alınır. Aktarım Chrome TLS taklidi kullanır (`curl-cffi`, `chrome120`); istekler ~0,8 sn arayla; her şey salt okunurdur.
53
+
54
+ ## Kurulum
55
+
56
+ ```bash
57
+ uvx tff-mcp # doğrudan çalıştır (uv gerekir)
58
+ # veya
59
+ pip install tff-mcp
60
+ tff-mcp # MCP sunucusunu stdio üzerinde başlatır
61
+ ```
62
+
63
+ ## Testler
64
+
65
+ ```bash
66
+ python -m venv .venv && .venv/bin/pip install -e .
67
+ .venv/bin/python -m unittest discover -s tests -v
68
+ ```
69
+
70
+ Çevrimdışı testler kayıtlı fiksçırlarla çalışır (gerçek sayfalar 2026-11-09'da çekildi); canlı testler tff.org'a gider. Derleme anında 37/37 yeşil (20 çevrimdışı + 17 canlı) ve bir MCP stdio duman testi.
71
+
72
+ ## Yol haritası
73
+
74
+ - Disiplin kararları (PFDK/AFDK sayfaları) ve cezalı oyuncular (pageID=633)
75
+ - Kulüp sayfaları (pageID=28&kulupID=...) — kadrolar
76
+ - Millî takım maçları (pageID=425)
77
+
78
+ ## Lisans
79
+
80
+ MIT — bkz. [LICENSE](LICENSE).
@@ -0,0 +1,80 @@
1
+ # tff-mcp 🇹🇷
2
+
3
+ mcp-name: io.github.mohammad-abbasi-sh/tff-mcp
4
+
5
+ Unofficial **read-only** MCP server for tff.org, the Turkish Football Federation (TFF) — league standings (puan cetveli), fixtures and results (fikstür), full match details, and top scorers (gol krallığı) for 18 Turkish leagues, from the Süper Lig down to the youth development leagues.
6
+
7
+ > Built by Mohammad Abbasi as part of the "Turkey MCP factory" (Sepehr-style pipeline). Not affiliated with tff.org or the TFF.
8
+
9
+ ## For AI agents
10
+
11
+ Add to your MCP client config (stdio):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "tff": {
17
+ "command": "uvx",
18
+ "args": ["tff-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Or run locally: `python -m tff_mcp.server` (needs `mcp<2`, `curl-cffi`).
25
+
26
+ ## Tools
27
+
28
+ | Tool | Description |
29
+ |------|-------------|
30
+ | `tff_list_leagues` | The 18 covered leagues (slug, Turkish name, TFF page id, multi-group flag, top-scorers availability) |
31
+ | `tff_standings` | League standings — `league` slug (default "trendyol-super-lig"); `group` for multi-group leagues: index ("0"), group id, or name ("Kırmızı", "Beyaz", "01"); rank, team, played/won/drawn/lost, goals for/against, goal difference, points |
32
+ | `tff_fixtures` | Fixtures and results — `league` slug; `week` to filter to one match-week; `group` for multi-group leagues; home/away, score (null when not yet played), status ("played"/"scheduled"), TFF match id and match-detail URL |
33
+ | `tff_match_detail` | Full detail of one match — `mac_id` (as returned by `tff_fixtures`, e.g. "317874"); teams and score, date/time, stadium, league/season, match code, the officiating team (referee, assistants, VAR/AVAR, observer, representative), goals with minutes, both starting XIs |
34
+ | `tff_top_scorers` | Top scorers — `league`: "trendyol-super-lig" (default), "trendyol-1-lig", "nesine-2-lig" or "nesine-3-lig" (the four leagues with a TFF scorers page); `limit` 1..50 |
35
+
36
+ Example output (`tff_match_detail` / Gaziantep FK 0–2 Kasımpaşa, 6 Nov 2026):
37
+
38
+ ```json
39
+ {"home": "GAZİANTEP FUTBOL KULÜBÜ A.Ş.", "away": "KASIMPAŞA A.Ş.",
40
+ "score_home": 0, "score_away": 2, "date": "6.11.2026", "time": "20:00",
41
+ "stadium": "GAZİANTEP STADYUMU - GAZİANTEP - ŞEHİTKAMİL",
42
+ "league": "Trendyol Süper Lig Adnan Süvari Sezonu (Profesyonel Takım)",
43
+ "match_code": "1091",
44
+ "officials": [{"name": "ÜMİT ÖZTÜRK", "role": "Hakem"},
45
+ {"name": "ERKAN ENGİN", "role": "VAR"}],
46
+ "goals": [{"player": "HARIS HAJRADINOVIC", "minute": 3, "type": "F"},
47
+ {"player": "ADRIAN DAWID BENEDYCZAK", "minute": 21, "type": "P"}]}
48
+ ```
49
+
50
+ ## How it works
51
+
52
+ tff.org serves its league pages fully server-rendered — no login, no API key, no session cookies. Standings live in `<table class="s-table">` (header O G B M A Y AV P); fixtures in `<table class="fiksturListesiTable">` ("N.Hafta" headers, match rows with home club link, score link `pageID=29&macId=...`, away club link — unplayed matches show " - "). Multi-group leagues (Nesine 2. Lig, Nesine 3. Lig, BAL, women's and youth leagues) expose their groups as `?pageID=<id>&grupID=<n>` links. Match details carry the teams in `lnkTakim1`/`lnkTakim2`, scores in `MacDetaySayi` spans, league/season in `lblOrganizasyonAdi`, match code in `lblKod`, date/time in `lblTarih`, the stadium as text between the code and the date, officials as `NAME(ROLE)` pairs, goals as "NAME,3.dk (F)" entries, and the starting XIs in the `grdTakim1/2_rptKadrolar` repeaters. Top scorers come from the `rptGolKralligi` repeater (pages 821/822/823/824). The site is legacy ASP.NET served as windows-1254, decoded explicitly. One site feature is documented, not faked: the weekly referees page (`pageID=600`) lists the 2./3. Lig rounds with EMPTY referee columns until appointments are announced close to match day — so referee data comes from the match-detail page instead. Transport uses Chrome TLS impersonation (`curl-cffi`, `chrome120`); requests are spaced ~0.8s apart; everything is read-only.
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ uvx tff-mcp # run directly (needs uv)
58
+ # or
59
+ pip install tff-mcp
60
+ tff-mcp # starts the MCP server on stdio
61
+ ```
62
+
63
+ ## Tests
64
+
65
+ ```bash
66
+ python -m venv .venv && .venv/bin/pip install -e .
67
+ .venv/bin/python -m unittest discover -s tests -v
68
+ ```
69
+
70
+ Offline tests run on recorded fixtures (real pages fetched 2026-11-09); live tests hit tff.org. 37/37 green at build time (20 offline + 17 live) plus an MCP stdio smoke test.
71
+
72
+ ## Roadmap
73
+
74
+ - Disciplinary decisions (PFDK/AFDK pages) and suspended players (pageID=633)
75
+ - Club pages (pageID=28&kulupID=...) — squad lists
76
+ - National-team matches (pageID=425)
77
+
78
+ ## License
79
+
80
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tff-mcp"
7
+ version = "0.1.0"
8
+ description = "Unofficial read-only MCP server for tff.org (Turkish Football Federation): standings, fixtures, match details, scorers"
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
+ tff-mcp = "tff_mcp.server:main"
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["src/tff_mcp"]
@@ -0,0 +1 @@
1
+ {"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "description": "Unofficial read-only MCP server for tff.org: league standings, fixtures and match details.", "name": "io.github.mohammad-abbasi-sh/tff-mcp", "packages": [{"identifier": "tff-mcp", "registryBaseUrl": "https://pypi.org", "registryType": "pypi", "transport": {"type": "stdio"}, "version": "0.1.0"}], "repository": {"source": "github", "url": "https://github.com/mohammad-abbasi-sh/tff-mcp"}, "title": "tff.org (Turkish Football Federation)", "version": "0.1.0", "websiteUrl": "https://github.com/mohammad-abbasi-sh/tff-mcp"}
@@ -0,0 +1 @@
1
+ """TFF (Turkish Football Federation) MCP server package."""