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.
- tff_mcp-0.1.0/.github/workflows/publish.yml +20 -0
- tff_mcp-0.1.0/.github/workflows/registry.yml +61 -0
- tff_mcp-0.1.0/LICENSE +21 -0
- tff_mcp-0.1.0/PKG-INFO +92 -0
- tff_mcp-0.1.0/README-fa.md +80 -0
- tff_mcp-0.1.0/README-tr.md +80 -0
- tff_mcp-0.1.0/README.md +80 -0
- tff_mcp-0.1.0/pyproject.toml +22 -0
- tff_mcp-0.1.0/server.json +1 -0
- tff_mcp-0.1.0/src/tff_mcp/__init__.py +1 -0
- tff_mcp-0.1.0/src/tff_mcp/football.py +454 -0
- tff_mcp-0.1.0/src/tff_mcp/http.py +87 -0
- tff_mcp-0.1.0/src/tff_mcp/server.py +220 -0
- tff_mcp-0.1.0/tests/__init__.py +0 -0
- tff_mcp-0.1.0/tests/fixtures/league-2lig.html +5601 -0
- tff_mcp-0.1.0/tests/fixtures/league-superlig.html +3713 -0
- tff_mcp-0.1.0/tests/fixtures/match-detail.html +1533 -0
- tff_mcp-0.1.0/tests/fixtures/scorers-superlig.html +1845 -0
- tff_mcp-0.1.0/tests/test_football.py +227 -0
- tff_mcp-0.1.0/tests/test_live.py +157 -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
|
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).
|
tff_mcp-0.1.0/README.md
ADDED
|
@@ -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."""
|