@claudinho/mcp 0.4.1 → 0.4.3
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.
- package/README.md +36 -73
- package/dist/index.js +79 -51
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @claudinho/mcp ⚽
|
|
2
2
|
|
|
3
|
-
**An MCP server for the 2026 men's football tournament.** Ask your agent about
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
**An MCP server for the 2026 men's football tournament.** Ask your agent about live
|
|
4
|
+
scores, fixtures, group standings, and the prediction-market read — in Claude Code,
|
|
5
|
+
Cursor, Codex, Claude Desktop, Windsurf, Zed, VS Code, or any MCP client (stdio).
|
|
6
6
|
|
|
7
7
|
> ⚠️ **Not affiliated with, endorsed by, or connected to FIFA or Anthropic.**
|
|
8
8
|
> Claudinho is an independent, open-source fan project. Factual match data with
|
|
@@ -15,29 +15,18 @@ Code, Cursor, Codex, and any other MCP client.
|
|
|
15
15
|
claude mcp add claudinho -- npx -y @claudinho/mcp
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
**
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Codex CLI** — add to `~/.codex/config.toml`:
|
|
24
|
-
```toml
|
|
25
|
-
[mcp_servers.claudinho]
|
|
26
|
-
command = "npx"
|
|
27
|
-
args = ["-y", "@claudinho/mcp"]
|
|
18
|
+
**Codex CLI**
|
|
19
|
+
```bash
|
|
20
|
+
codex mcp add claudinho -- npx -y @claudinho/mcp
|
|
28
21
|
```
|
|
29
|
-
(or `codex mcp add claudinho -- npx -y @claudinho/mcp`)
|
|
30
22
|
|
|
31
|
-
**Claude Desktop
|
|
32
|
-
Edit Config), then restart Claude Desktop:
|
|
23
|
+
**Cursor / Claude Desktop / Windsurf / Zed / VS Code** — standard stdio config:
|
|
33
24
|
```json
|
|
34
25
|
{ "mcpServers": { "claudinho": { "command": "npx", "args": ["-y", "@claudinho/mcp"] } } }
|
|
35
26
|
```
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Transport is stdio, so the same package works in Windsurf, Zed, VS Code, and
|
|
40
|
-
any other MCP client with no changes.
|
|
27
|
+
Cursor: `.cursor/mcp.json` (or `~/.cursor/mcp.json`). Claude Desktop: Settings →
|
|
28
|
+
Developer → Edit Config, then restart. Codex config file: `~/.codex/config.toml`
|
|
29
|
+
(`[mcp_servers.claudinho]`, `command = "npx"`, `args = ["-y", "@claudinho/mcp"]`).
|
|
41
30
|
|
|
42
31
|
## Tools
|
|
43
32
|
|
|
@@ -47,67 +36,41 @@ any other MCP client with no changes.
|
|
|
47
36
|
| `get_live` | matches in play right now |
|
|
48
37
|
| `get_match` | a single match by id |
|
|
49
38
|
| `get_standings` | group table(s) — one group `A`–`L`, or all |
|
|
50
|
-
| `get_next_fixture` | a team's next match (3-letter code, e.g. `MEX`) |
|
|
51
|
-
| `get_market_signal` | read-only prediction-market
|
|
52
|
-
| `get_share_snippet` | a copy-pasteable
|
|
53
|
-
|
|
54
|
-
All tools are **read-only** (annotated `readOnlyHint`) and accept optional
|
|
55
|
-
`tz` (IANA timezone), `lang` (`en`/`es`/`pt`/`fr`), and `flavor`
|
|
56
|
-
(`off`/`subtle`/`full`) arguments. Each response includes both human-readable
|
|
57
|
-
text and structured JSON.
|
|
58
|
-
|
|
59
|
-
`get_today` and `get_match` also include reliable prediction-market context when a
|
|
60
|
-
market is available. Match→market event slugs are derived automatically, so no
|
|
61
|
-
mapping is needed. Market data is **read-only and informational only — not betting
|
|
62
|
-
advice** (market-implied percentages with attribution, never links or trade
|
|
63
|
-
calls), sourced from Polymarket public data and shown only when a market maps
|
|
64
|
-
cleanly to the result. Disable it with `CLAUDINHO_MARKETS=off`; set
|
|
65
|
-
`CLAUDINHO_MARKETS_SOURCE=fake` (in the server `env`) for a network-free synthetic
|
|
66
|
-
preview.
|
|
67
|
-
|
|
68
|
-
`get_share_snippet` returns a polished, **copy-pasteable** match card (the same
|
|
69
|
-
artifact as the CLI's `claudinho share`) for a match, a team's next fixture, a
|
|
70
|
-
date, or live matches — plain text, no links, with the non-affiliation disclaimer
|
|
71
|
-
baked in. Hand the returned `snippet` to the user as-is. `style` picks `social`
|
|
72
|
-
(default) or `compact`; market lines (when reliable) stay **informational only**.
|
|
73
|
-
|
|
74
|
-
## Resources & prompts
|
|
75
|
-
|
|
76
|
-
- Resources: `standings://{group}` (e.g. `standings://A`), `fixtures://{date}` (UTC date, e.g. `fixtures://2026-06-11`)
|
|
77
|
-
- Prompts: `tournament_today`, `my_team`
|
|
39
|
+
| `get_next_fixture` | a team's next match (3-letter code, e.g. `MEX`) — fully offline |
|
|
40
|
+
| `get_market_signal` | read-only prediction-market signal for a match, a team's current-or-next fixture (in-play preferred while live), or a date — informational only |
|
|
41
|
+
| `get_share_snippet` | a copy-pasteable plain-text match card — hand the returned snippet to the user as-is |
|
|
78
42
|
|
|
79
|
-
|
|
43
|
+
All tools are **read-only** (`readOnlyHint`) and accept optional `tz`, `lang`
|
|
44
|
+
(`en`/`es`/`pt`/`fr`), and `flavor` (`off`/`subtle`/`full`). Every response carries
|
|
45
|
+
human-readable text **and** structured JSON.
|
|
80
46
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
energy. The phrases are generic — no real commentator is quoted or impersonated —
|
|
85
|
-
and they never touch the structured JSON, so the facts stay clean.
|
|
47
|
+
Resources: `standings://{group}`, `fixtures://{date}`. Prompts: `tournament_today`,
|
|
48
|
+
and `my_team` (give it a 3-letter team code; combines next fixture, standings, and
|
|
49
|
+
the prediction-market read).
|
|
86
50
|
|
|
87
|
-
|
|
88
|
-
or per call via the `flavor` tool argument. In Claude Code, add it to the `env`
|
|
89
|
-
block of your server entry:
|
|
51
|
+
## Market signals
|
|
90
52
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
53
|
+
Market signals are pre-match and in-play reads — finished matches never show one.
|
|
54
|
+
`get_today` / `get_match` include a short market line when a reliable market exists
|
|
55
|
+
(slugs are auto-derived per fixture; matching fails closed). **Read-only and
|
|
56
|
+
informational only — not betting advice:** market-implied percentages with Polymarket
|
|
57
|
+
attribution, never links or trade calls. Disable with `CLAUDINHO_MARKETS=off`; set
|
|
58
|
+
`CLAUDINHO_MARKETS_SOURCE=fake` in the server `env` for a network-free, clearly
|
|
59
|
+
labeled synthetic preview.
|
|
97
60
|
|
|
98
|
-
##
|
|
61
|
+
## Commentary flair
|
|
99
62
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
63
|
+
Match lines in the text end with a short, localized, genre-style exclamation
|
|
64
|
+
(`— ¡GOOOOL!`) — generic energy, no real commentator quoted or impersonated, never
|
|
65
|
+
in the structured JSON. Control with `CLAUDINHO_FLAVOR` (`off`|`subtle`|`full`,
|
|
66
|
+
default `full`) in the server `env`, or per call via the `flavor` argument.
|
|
104
67
|
|
|
105
68
|
## How it works
|
|
106
69
|
|
|
107
|
-
|
|
108
|
-
live scores from **ESPN's** public scoreboard (
|
|
109
|
-
|
|
110
|
-
|
|
70
|
+
All 104 fixtures ship bundled in the package; only live state hits the network —
|
|
71
|
+
live scores from **ESPN's** public scoreboard (swappable provider, attributed in
|
|
72
|
+
output), market signals from Polymarket. Stdout carries only the MCP protocol;
|
|
73
|
+
diagnostics go to stderr.
|
|
111
74
|
|
|
112
75
|
## License
|
|
113
76
|
|
package/dist/index.js
CHANGED
|
@@ -2614,6 +2614,7 @@ function nextFixtureForTeam(code, opts = {}) {
|
|
|
2614
2614
|
(m) => new Date(m.kickoff).getTime() >= from.getTime()
|
|
2615
2615
|
);
|
|
2616
2616
|
}
|
|
2617
|
+
var LIVE_WINDOW_MS = 140 * 6e4;
|
|
2617
2618
|
function groups(fixtures = SCHEDULE) {
|
|
2618
2619
|
const set = /* @__PURE__ */ new Set();
|
|
2619
2620
|
for (const m of fixtures) if (m.group) set.add(m.group);
|
|
@@ -2871,6 +2872,33 @@ function shiftUtcDate(dateISO, days) {
|
|
|
2871
2872
|
const [y, m, d] = dateISO.slice(0, 10).split("-").map(Number);
|
|
2872
2873
|
return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
|
|
2873
2874
|
}
|
|
2875
|
+
var EXTRA_TIME_SLACK_MS = 60 * 6e4;
|
|
2876
|
+
async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Date()) {
|
|
2877
|
+
const nowMs = now.getTime();
|
|
2878
|
+
const candidate = fixturesByTeam(code).find((m) => {
|
|
2879
|
+
const k = Date.parse(m.kickoff);
|
|
2880
|
+
return nowMs >= k && nowMs <= k + LIVE_WINDOW_MS + EXTRA_TIME_SLACK_MS;
|
|
2881
|
+
});
|
|
2882
|
+
if (candidate) {
|
|
2883
|
+
const r = await getMatchById(adapter, candidate.id);
|
|
2884
|
+
const m = r.match ?? candidate;
|
|
2885
|
+
if (!isFinished(m.status)) return { ...r, match: m };
|
|
2886
|
+
}
|
|
2887
|
+
const next = nextFixtureForTeam(code, { from: now });
|
|
2888
|
+
return { match: next, degraded: false };
|
|
2889
|
+
}
|
|
2890
|
+
async function getMatchById(adapter, id) {
|
|
2891
|
+
const base = allFixtures().find((m) => m.id === id);
|
|
2892
|
+
if (!base) return { match: void 0, degraded: false };
|
|
2893
|
+
const day = base.kickoff.slice(0, 10);
|
|
2894
|
+
try {
|
|
2895
|
+
const live = adapter.fetchWindow ? await adapter.fetchWindow(shiftUtcDate(day, -1), shiftUtcDate(day, 1)) : await adapter.fetchByDate(day);
|
|
2896
|
+
const hit = live.find((m) => m.id === id);
|
|
2897
|
+
return { match: hit ?? base, degraded: false, source: hit ? adapter.name : void 0 };
|
|
2898
|
+
} catch {
|
|
2899
|
+
return { match: base, degraded: true };
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2874
2902
|
async function getLiveMatches(adapter) {
|
|
2875
2903
|
try {
|
|
2876
2904
|
return { matches: await adapter.fetchLive(), degraded: false, source: adapter.name };
|
|
@@ -2879,6 +2907,12 @@ async function getLiveMatches(adapter) {
|
|
|
2879
2907
|
}
|
|
2880
2908
|
}
|
|
2881
2909
|
var DEFAULT_MAX_AGE_MS = 15 * 6e4;
|
|
2910
|
+
function marketRelevant(match, now = /* @__PURE__ */ new Date()) {
|
|
2911
|
+
if (isLive(match.status)) return true;
|
|
2912
|
+
if (isFinished(match.status)) return false;
|
|
2913
|
+
const k = Date.parse(match.kickoff);
|
|
2914
|
+
return !Number.isFinite(k) || now.getTime() <= k + LIVE_WINDOW_MS;
|
|
2915
|
+
}
|
|
2882
2916
|
function normalizeOutcomes(outcomes) {
|
|
2883
2917
|
const sum = outcomes.reduce(
|
|
2884
2918
|
(s, o) => s + (Number.isFinite(o.probability) && o.probability > 0 ? o.probability : 0),
|
|
@@ -3454,13 +3488,19 @@ function resolveMarketProvider(args) {
|
|
|
3454
3488
|
function marketDisplayable(sig) {
|
|
3455
3489
|
return !sig.ambiguous && sig.favorite != null && hasSaneDistribution(sig.outcomes);
|
|
3456
3490
|
}
|
|
3457
|
-
function marketHeader(m) {
|
|
3458
|
-
|
|
3491
|
+
function marketHeader(m, args) {
|
|
3492
|
+
const when = formatDate(m.kickoff, { tz: args.tz, locale: args.lang });
|
|
3493
|
+
return `${m.home.flag} ${m.home.name} vs ${m.away.name} ${m.away.flag} (${when})`;
|
|
3459
3494
|
}
|
|
3460
|
-
function marketText(m, sig) {
|
|
3461
|
-
return `${marketHeader(m)}
|
|
3495
|
+
function marketText(m, sig, args) {
|
|
3496
|
+
return `${marketHeader(m, args)}
|
|
3462
3497
|
${marketBlock(sig, m).join("\n")}`;
|
|
3463
3498
|
}
|
|
3499
|
+
function noSignalText(m, args, now) {
|
|
3500
|
+
if (marketRelevant(m, now)) return `No reliable market signal for ${marketHeader(m, args)}.`;
|
|
3501
|
+
const verb = isFinished(m.status) ? "has finished" : "appears to have finished";
|
|
3502
|
+
return `${marketHeader(m, args)} ${verb} \u2014 market signals are pre-match and in-play reads.`;
|
|
3503
|
+
}
|
|
3464
3504
|
function marketData(sig) {
|
|
3465
3505
|
return {
|
|
3466
3506
|
matchId: sig.matchId,
|
|
@@ -3521,10 +3561,12 @@ async function cachedMarketSignals(args, matches) {
|
|
|
3521
3561
|
}
|
|
3522
3562
|
async function reliableMarketData(args, matches) {
|
|
3523
3563
|
if (!marketsEnabled()) return void 0;
|
|
3524
|
-
const
|
|
3525
|
-
const
|
|
3564
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
3565
|
+
const relevant = matches.filter((m) => marketRelevant(m, now));
|
|
3566
|
+
if (relevant.length === 0) return void 0;
|
|
3567
|
+
const signals = await cachedMarketSignals(args, relevant);
|
|
3526
3568
|
const out = {};
|
|
3527
|
-
for (const m of
|
|
3569
|
+
for (const m of relevant) {
|
|
3528
3570
|
const s = signals.get(m.id);
|
|
3529
3571
|
if (s && isReliableMarketSignal(s, { now })) out[m.id] = marketData(s);
|
|
3530
3572
|
}
|
|
@@ -3577,27 +3619,16 @@ ${matchList(matches, "No matches in play right now.", opts)}`;
|
|
|
3577
3619
|
};
|
|
3578
3620
|
}
|
|
3579
3621
|
async function toolGetMatch(args) {
|
|
3580
|
-
|
|
3581
|
-
let degraded = false;
|
|
3582
|
-
let liveSource;
|
|
3583
|
-
if (match) {
|
|
3584
|
-
try {
|
|
3585
|
-
const adapter = resolveAdapter(args);
|
|
3586
|
-
const live = await adapter.fetchByDate(match.kickoff.slice(0, 10));
|
|
3587
|
-
match = live.find((m) => m.id === args.id) ?? match;
|
|
3588
|
-
liveSource = adapter.name;
|
|
3589
|
-
} catch {
|
|
3590
|
-
degraded = true;
|
|
3591
|
-
}
|
|
3592
|
-
}
|
|
3622
|
+
const { match, degraded, source: liveSource } = await getMatchById(resolveAdapter(args), args.id);
|
|
3593
3623
|
if (!match) {
|
|
3594
3624
|
return { text: withDisclaimer(`No match found with id ${args.id}.`), data: { match: null } };
|
|
3595
3625
|
}
|
|
3596
3626
|
const opts = fmtOpts(args);
|
|
3627
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
3597
3628
|
let marketSignal;
|
|
3598
|
-
if (marketsEnabled()) {
|
|
3629
|
+
if (marketsEnabled() && marketRelevant(match, now)) {
|
|
3599
3630
|
const s = (await cachedMarketSignals(args, [match])).get(match.id);
|
|
3600
|
-
if (s && isReliableMarketSignal(s, { now
|
|
3631
|
+
if (s && isReliableMarketSignal(s, { now })) marketSignal = s;
|
|
3601
3632
|
}
|
|
3602
3633
|
const base = matchLine(match, opts);
|
|
3603
3634
|
const text = marketSignal ? `${base}
|
|
@@ -3656,11 +3687,13 @@ ${matchLine(fixture, opts)}`),
|
|
|
3656
3687
|
}
|
|
3657
3688
|
async function toolGetMarketSignal(args) {
|
|
3658
3689
|
const provider = resolveMarketProvider(args);
|
|
3690
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
3659
3691
|
if (args.matchId) {
|
|
3660
|
-
const match =
|
|
3661
|
-
const
|
|
3692
|
+
const { match } = await getMatchById(resolveAdapter(args), args.matchId);
|
|
3693
|
+
const relevant = match ? marketRelevant(match, now) : false;
|
|
3694
|
+
const sig = match && relevant ? await getMarketSignal(provider, match) : void 0;
|
|
3662
3695
|
const shown2 = match && sig && marketDisplayable(sig) ? sig : void 0;
|
|
3663
|
-
const text2 = !match ? `No match found with id ${args.matchId}.` : shown2 ? marketText(match, shown2) :
|
|
3696
|
+
const text2 = !match ? `No match found with id ${args.matchId}.` : shown2 ? marketText(match, shown2, args) : noSignalText(match, args, now);
|
|
3664
3697
|
return {
|
|
3665
3698
|
text: withDisclaimer(text2),
|
|
3666
3699
|
data: {
|
|
@@ -3672,10 +3705,11 @@ async function toolGetMarketSignal(args) {
|
|
|
3672
3705
|
}
|
|
3673
3706
|
if (args.team) {
|
|
3674
3707
|
const code = args.team.toUpperCase();
|
|
3675
|
-
const fixture =
|
|
3676
|
-
const
|
|
3708
|
+
const { match: fixture } = await marketFixtureForTeam(resolveAdapter(args), code, now);
|
|
3709
|
+
const relevant = fixture ? marketRelevant(fixture, now) : false;
|
|
3710
|
+
const sig = fixture && relevant ? await getMarketSignal(provider, fixture) : void 0;
|
|
3677
3711
|
const shown2 = fixture && sig && marketDisplayable(sig) ? sig : void 0;
|
|
3678
|
-
const text2 = !fixture ? `No upcoming fixture found for ${code}.` : shown2 ? marketText(fixture, shown2) :
|
|
3712
|
+
const text2 = !fixture ? `No upcoming fixture found for ${code}.` : shown2 ? marketText(fixture, shown2, args) : noSignalText(fixture, args, now);
|
|
3679
3713
|
return {
|
|
3680
3714
|
text: withDisclaimer(text2),
|
|
3681
3715
|
data: {
|
|
@@ -3686,15 +3720,15 @@ async function toolGetMarketSignal(args) {
|
|
|
3686
3720
|
}
|
|
3687
3721
|
};
|
|
3688
3722
|
}
|
|
3689
|
-
const date = args.date ?? localDate(
|
|
3723
|
+
const date = args.date ?? localDate(now.toISOString(), args.tz);
|
|
3690
3724
|
const { matches } = await getMatchesForDate(resolveAdapter(args), date);
|
|
3691
|
-
const todays = fixturesByDate(date, matches, args.tz);
|
|
3725
|
+
const todays = fixturesByDate(date, matches, args.tz).filter((m) => marketRelevant(m, now));
|
|
3692
3726
|
const { signals } = await getMarketSignals(provider, todays, MARKETS_TOOL_OPTS);
|
|
3693
3727
|
const shown = todays.map((m) => ({ match: m, signal: signals.get(m.id) })).filter(
|
|
3694
3728
|
(r) => !!r.signal && marketDisplayable(r.signal)
|
|
3695
3729
|
);
|
|
3696
3730
|
const text = shown.length ? `Market signals on ${date}:
|
|
3697
|
-
${shown.map(({ match, signal }) => marketText(match, signal)).join("\n\n")}` : `No reliable market signals on ${date}.`;
|
|
3731
|
+
${shown.map(({ match, signal }) => marketText(match, signal, args)).join("\n\n")}` : `No reliable market signals on ${date}.`;
|
|
3698
3732
|
return {
|
|
3699
3733
|
text: withDisclaimer(text),
|
|
3700
3734
|
data: {
|
|
@@ -3706,10 +3740,12 @@ ${shown.map(({ match, signal }) => marketText(match, signal)).join("\n\n")}` : `
|
|
|
3706
3740
|
}
|
|
3707
3741
|
async function reliableSignalMap(args, matches) {
|
|
3708
3742
|
if (!marketsEnabled()) return /* @__PURE__ */ new Map();
|
|
3709
|
-
const
|
|
3710
|
-
const
|
|
3743
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
3744
|
+
const relevant = matches.filter((m) => marketRelevant(m, now));
|
|
3745
|
+
if (relevant.length === 0) return /* @__PURE__ */ new Map();
|
|
3746
|
+
const signals = await cachedMarketSignals(args, relevant);
|
|
3711
3747
|
const out = /* @__PURE__ */ new Map();
|
|
3712
|
-
for (const m of
|
|
3748
|
+
for (const m of relevant) {
|
|
3713
3749
|
const s = signals.get(m.id);
|
|
3714
3750
|
if (s && isReliableMarketSignal(s, { now }) && marketDisplayable(s)) out.set(m.id, s);
|
|
3715
3751
|
}
|
|
@@ -3771,17 +3807,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3771
3807
|
);
|
|
3772
3808
|
}
|
|
3773
3809
|
if (args.matchId) {
|
|
3774
|
-
|
|
3775
|
-
let source2;
|
|
3776
|
-
if (match) {
|
|
3777
|
-
try {
|
|
3778
|
-
const adapter = resolveAdapter(args);
|
|
3779
|
-
const live = await adapter.fetchByDate(match.kickoff.slice(0, 10));
|
|
3780
|
-
match = live.find((m) => m.id === args.matchId) ?? match;
|
|
3781
|
-
source2 = adapter.name;
|
|
3782
|
-
} catch {
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3810
|
+
const { match, source: source2 } = await getMatchById(resolveAdapter(args), args.matchId);
|
|
3785
3811
|
const matches = match ? [match] : [];
|
|
3786
3812
|
return shareResult(
|
|
3787
3813
|
"match",
|
|
@@ -3845,12 +3871,12 @@ async function toolGetShareSnippet(args) {
|
|
|
3845
3871
|
|
|
3846
3872
|
// src/server.ts
|
|
3847
3873
|
var SERVER_NAME = "claudinho";
|
|
3848
|
-
var SERVER_VERSION = "0.4.
|
|
3874
|
+
var SERVER_VERSION = "0.4.3";
|
|
3849
3875
|
var VOICE = asFlavorLevel(process.env.CLAUDINHO_FLAVOR) === "off" ? "" : `
|
|
3850
3876
|
Voice: when relaying scores, narrate with lively, regionally-appropriate football-commentary energy in the user's language. Each match line may end with a short exclamation ("\u2014 \xA1GOOOOL!") \u2014 use it as a tone cue. Keep every fact exact; never invent details and never impersonate or name a real commentator.`;
|
|
3851
3877
|
var INSTRUCTIONS = `Claudinho serves live scores, fixtures, and group standings for the 2026 men's football tournament.
|
|
3852
3878
|
Use get_live during matches, get_today for a day's schedule, get_next_fixture for a specific team (3-letter code, e.g. MEX), and get_standings for group tables.
|
|
3853
|
-
Use get_market_signal for read-only prediction-market
|
|
3879
|
+
Use get_market_signal for read-only prediction-market signals (a match, a team's current-or-next fixture, or a date). Market data is informational only \u2014 relay the percentages factually and never frame it as betting or trading advice.
|
|
3854
3880
|
Use get_share_snippet to produce a ready-to-paste match card (for a match, a team's next fixture, a date, or live matches) \u2014 hand the user the returned snippet text verbatim.${VOICE}
|
|
3855
3881
|
${DISCLAIMER}`;
|
|
3856
3882
|
var dateArg = z.string().refine(isValidDate, "must be a real calendar date in YYYY-MM-DD form");
|
|
@@ -3926,7 +3952,7 @@ function buildServer() {
|
|
|
3926
3952
|
"get_next_fixture",
|
|
3927
3953
|
{
|
|
3928
3954
|
title: "Next fixture for a team",
|
|
3929
|
-
description: "A team's next scheduled match. Use a 3-letter code, e.g. MEX, BRA, USA.",
|
|
3955
|
+
description: "A team's next scheduled match. Use a 3-letter code, e.g. MEX, BRA, USA. Instant and offline \u2014 answered from the bundled schedule, no network.",
|
|
3930
3956
|
inputSchema: { team: teamArg.describe("3-letter team code, e.g. MEX"), ...commonArgs },
|
|
3931
3957
|
// Read-only and served entirely from the bundled static schedule.
|
|
3932
3958
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
@@ -3937,10 +3963,12 @@ function buildServer() {
|
|
|
3937
3963
|
"get_market_signal",
|
|
3938
3964
|
{
|
|
3939
3965
|
title: "Prediction-market signal",
|
|
3940
|
-
description: "Read-only prediction-market
|
|
3966
|
+
description: "Read-only prediction-market signals for a match (by id), a team's current-or-next fixture, or a date (default: today). Returns market-implied percentages with attribution. Shown only before and during a match \u2014 finished matches have no market read. Informational only \u2014 relay the numbers factually; do not add betting, trading, or 'value' advice, and do not invent links.",
|
|
3941
3967
|
inputSchema: {
|
|
3942
3968
|
matchId: z.string().optional().describe("Match id (most specific)"),
|
|
3943
|
-
team: teamArg.optional().describe(
|
|
3969
|
+
team: teamArg.optional().describe(
|
|
3970
|
+
"3-letter team code, e.g. MEX \u2014 resolves to the team's in-play match when one is live, else their next fixture"
|
|
3971
|
+
),
|
|
3944
3972
|
date: dateArg.optional().describe("Date as YYYY-MM-DD (default: today) for all that day's signals"),
|
|
3945
3973
|
...commonArgs
|
|
3946
3974
|
},
|
|
@@ -4032,7 +4060,7 @@ function buildServer() {
|
|
|
4032
4060
|
role: "user",
|
|
4033
4061
|
content: {
|
|
4034
4062
|
type: "text",
|
|
4035
|
-
text: `Using get_next_fixture, get_standings, and get_market_signal, tell me about ${team}'s next match in the 2026 tournament, their current group standing, and what prediction markets currently say about that match. Treat the market percentages as informational context only \u2014 relay them factually, never as betting or trading advice.`
|
|
4063
|
+
text: `Using get_next_fixture, get_standings, and get_market_signal, tell me about ${team}'s next match in the 2026 tournament, their current group standing, and what prediction markets currently say about that match. Always state each fixture's date so a market read is never mistaken for a different match. Treat the market percentages as informational context only \u2014 relay them factually, never as betting or trading advice.`
|
|
4036
4064
|
}
|
|
4037
4065
|
}
|
|
4038
4066
|
]
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/mcp",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"mcpName": "io.github.arturogarrido/claudinho",
|
|
5
|
+
"description": "MCP server for the 2026 men's football tournament — live scores, fixtures, standings, read-only prediction-market signals, and paste-ready match cards. Works with Claude Code, Cursor, Codex, Windsurf, Zed. Not affiliated with FIFA or Anthropic.",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"author": "Arturo Garrido",
|
|
@@ -30,12 +31,18 @@
|
|
|
30
31
|
],
|
|
31
32
|
"keywords": [
|
|
32
33
|
"mcp",
|
|
34
|
+
"mcp-server",
|
|
33
35
|
"model-context-protocol",
|
|
36
|
+
"claude-code",
|
|
37
|
+
"cursor",
|
|
38
|
+
"world-cup",
|
|
39
|
+
"world-cup-2026",
|
|
34
40
|
"football",
|
|
35
41
|
"soccer",
|
|
36
|
-
"world-cup",
|
|
37
|
-
"2026",
|
|
38
42
|
"live-scores",
|
|
43
|
+
"fixtures",
|
|
44
|
+
"standings",
|
|
45
|
+
"2026",
|
|
39
46
|
"vibinglavidaloca"
|
|
40
47
|
],
|
|
41
48
|
"dependencies": {
|
|
@@ -47,7 +54,7 @@
|
|
|
47
54
|
"tsup": "^8.0.0",
|
|
48
55
|
"typescript": "^5.7.0",
|
|
49
56
|
"vitest": "^4.1.0",
|
|
50
|
-
"@claudinho/core": "0.4.
|
|
57
|
+
"@claudinho/core": "0.4.3"
|
|
51
58
|
},
|
|
52
59
|
"scripts": {
|
|
53
60
|
"build": "tsup",
|