@claudinho/mcp 0.5.0 → 0.5.2
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/dist/index.js +36 -16
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2812,9 +2812,15 @@ var EspnAdapter = class {
|
|
|
2812
2812
|
async fetchWindow(startDate, endDate) {
|
|
2813
2813
|
return this.fetchScoreboard(`${toEspnDate(startDate)}-${toEspnDate(endDate)}`);
|
|
2814
2814
|
}
|
|
2815
|
+
/**
|
|
2816
|
+
* In-play matches from ESPN's DEFAULT scoreboard bucket only (no `dates`). This
|
|
2817
|
+
* single bucket misses a late kickoff ESPN files under an adjacent day, so it
|
|
2818
|
+
* is a fallback for window-less callers — the domain `getLiveMatches` wraps a
|
|
2819
|
+
* ±1-day window around this to catch boundary-crossing matches. Prefer it.
|
|
2820
|
+
*/
|
|
2815
2821
|
async fetchLive() {
|
|
2816
2822
|
const today = await this.fetchScoreboard();
|
|
2817
|
-
return today.filter((m) => m.status
|
|
2823
|
+
return today.filter((m) => isLive(m.status));
|
|
2818
2824
|
}
|
|
2819
2825
|
/** Standings endpoint URL (lives under apis/v2, not site/v2; derived from base). */
|
|
2820
2826
|
standingsUrl() {
|
|
@@ -2957,9 +2963,13 @@ async function getMatchById(adapter, id) {
|
|
|
2957
2963
|
return { match: base, degraded: true };
|
|
2958
2964
|
}
|
|
2959
2965
|
}
|
|
2960
|
-
async function getLiveMatches(adapter) {
|
|
2966
|
+
async function getLiveMatches(adapter, now = /* @__PURE__ */ new Date()) {
|
|
2961
2967
|
try {
|
|
2962
|
-
|
|
2968
|
+
const day = now.toISOString().slice(0, 10);
|
|
2969
|
+
const matches = adapter.fetchWindow ? (await adapter.fetchWindow(shiftUtcDate(day, -1), shiftUtcDate(day, 1))).filter(
|
|
2970
|
+
(m) => isLive(m.status)
|
|
2971
|
+
) : await adapter.fetchLive();
|
|
2972
|
+
return { matches, degraded: false, source: adapter.name };
|
|
2963
2973
|
} catch {
|
|
2964
2974
|
return { matches: [], degraded: true };
|
|
2965
2975
|
}
|
|
@@ -3465,6 +3475,9 @@ function formatShareSnippet(input, options = {}) {
|
|
|
3465
3475
|
blocks.push(card.join("\n"));
|
|
3466
3476
|
}
|
|
3467
3477
|
}
|
|
3478
|
+
if (input.degraded && input.matches.length > 0) {
|
|
3479
|
+
blocks.push("(Live data unavailable \u2014 showing the bundled schedule, not live scores.)");
|
|
3480
|
+
}
|
|
3468
3481
|
blocks.push(
|
|
3469
3482
|
shareFooter({
|
|
3470
3483
|
source: input.source,
|
|
@@ -3689,8 +3702,9 @@ async function toolGetToday(args) {
|
|
|
3689
3702
|
const { matches, degraded, source } = await getMatchesForDate(adapter, date);
|
|
3690
3703
|
const todays = fixturesByDate(date, matches, args.tz);
|
|
3691
3704
|
const opts = fmtOpts(args);
|
|
3692
|
-
|
|
3705
|
+
let text = `Matches on ${date}:
|
|
3693
3706
|
${matchList(todays, "No matches scheduled.", opts)}`;
|
|
3707
|
+
if (degraded) text += "\n\n(Live scores unavailable \u2014 showing the bundled schedule.)";
|
|
3694
3708
|
const marketSignals = await reliableMarketData(args, todays);
|
|
3695
3709
|
return {
|
|
3696
3710
|
text: withDisclaimer(text, source),
|
|
@@ -3708,7 +3722,7 @@ async function toolGetLive(args = {}) {
|
|
|
3708
3722
|
const adapter = resolveAdapter(args);
|
|
3709
3723
|
const { matches, degraded, source } = await getLiveMatches(adapter);
|
|
3710
3724
|
const opts = fmtOpts(args);
|
|
3711
|
-
const text = `Live now:
|
|
3725
|
+
const text = degraded ? "Live scores unavailable right now \u2014 could not reach the data provider." : `Live now:
|
|
3712
3726
|
${matchList(matches, "No matches in play right now.", opts)}`;
|
|
3713
3727
|
return {
|
|
3714
3728
|
text: withDisclaimer(text, source),
|
|
@@ -3728,8 +3742,9 @@ async function toolGetMatch(args) {
|
|
|
3728
3742
|
if (s && isReliableMarketSignal(s, { now })) marketSignal = s;
|
|
3729
3743
|
}
|
|
3730
3744
|
const base = matchLine(match, opts);
|
|
3731
|
-
|
|
3745
|
+
let text = marketSignal ? `${base}
|
|
3732
3746
|
${marketBlock(marketSignal, match).join("\n")}` : base;
|
|
3747
|
+
if (degraded) text += "\n\n(Live state unavailable \u2014 showing the scheduled fixture.)";
|
|
3733
3748
|
return {
|
|
3734
3749
|
text: withDisclaimer(text, liveSource),
|
|
3735
3750
|
data: {
|
|
@@ -3869,6 +3884,7 @@ function shareResult(kind, target, team, input, options) {
|
|
|
3869
3884
|
target,
|
|
3870
3885
|
...team ? { team } : {},
|
|
3871
3886
|
source: input.source ?? null,
|
|
3887
|
+
degraded: input.degraded ?? false,
|
|
3872
3888
|
informationalOnly: true,
|
|
3873
3889
|
style: options.style ?? "social",
|
|
3874
3890
|
snippet,
|
|
@@ -3886,7 +3902,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3886
3902
|
const options = shareOptions(args);
|
|
3887
3903
|
const signalsFor = (ms) => args.includeMarkets === false ? Promise.resolve(/* @__PURE__ */ new Map()) : reliableSignalMap(args, ms);
|
|
3888
3904
|
if (args.live) {
|
|
3889
|
-
const { matches, source: source2 } = await getLiveMatches(resolveAdapter(args));
|
|
3905
|
+
const { matches, degraded: degraded2, source: source2 } = await getLiveMatches(resolveAdapter(args));
|
|
3890
3906
|
return shareResult(
|
|
3891
3907
|
"live",
|
|
3892
3908
|
"live",
|
|
@@ -3895,7 +3911,9 @@ async function toolGetShareSnippet(args) {
|
|
|
3895
3911
|
title: "Live match pulse",
|
|
3896
3912
|
matches,
|
|
3897
3913
|
source: source2,
|
|
3898
|
-
|
|
3914
|
+
degraded: degraded2,
|
|
3915
|
+
// Feed down ⇒ don't let an empty card read as "nothing is on".
|
|
3916
|
+
emptyNote: degraded2 ? "Live scores unavailable right now \u2014 couldn't reach the data provider." : "No matches in play right now.",
|
|
3899
3917
|
installLine: "npx @claudinho/cli live",
|
|
3900
3918
|
tz: args.tz,
|
|
3901
3919
|
locale: args.lang
|
|
@@ -3905,16 +3923,16 @@ async function toolGetShareSnippet(args) {
|
|
|
3905
3923
|
}
|
|
3906
3924
|
if (args.group) {
|
|
3907
3925
|
const group = args.group.toUpperCase();
|
|
3908
|
-
const { tables, degraded, source: source2 } = await getStandings(resolveAdapter(args), group);
|
|
3926
|
+
const { tables, degraded: degraded2, source: source2 } = await getStandings(resolveAdapter(args), group);
|
|
3909
3927
|
const snippet = formatShareTable(
|
|
3910
3928
|
{
|
|
3911
3929
|
tables,
|
|
3912
3930
|
// Degraded ⇒ static roster, no live provider: don't attribute one, and
|
|
3913
3931
|
// surface the not-live notice (the card gets pasted publicly).
|
|
3914
|
-
source:
|
|
3932
|
+
source: degraded2 ? void 0 : source2,
|
|
3915
3933
|
installLine: `npx @claudinho/cli table ${group}`,
|
|
3916
3934
|
emptyNote: `No group ${group}.`,
|
|
3917
|
-
degraded
|
|
3935
|
+
degraded: degraded2
|
|
3918
3936
|
},
|
|
3919
3937
|
options
|
|
3920
3938
|
);
|
|
@@ -3924,8 +3942,8 @@ async function toolGetShareSnippet(args) {
|
|
|
3924
3942
|
kind: "table",
|
|
3925
3943
|
target: "table",
|
|
3926
3944
|
group,
|
|
3927
|
-
source:
|
|
3928
|
-
degraded,
|
|
3945
|
+
source: degraded2 ? null : source2 ?? null,
|
|
3946
|
+
degraded: degraded2,
|
|
3929
3947
|
informationalOnly: true,
|
|
3930
3948
|
snippet,
|
|
3931
3949
|
tables: tables.map((tb) => ({ group: tb.group, standings: tb.rows }))
|
|
@@ -3933,7 +3951,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3933
3951
|
};
|
|
3934
3952
|
}
|
|
3935
3953
|
if (args.matchId) {
|
|
3936
|
-
const { match, source: source2 } = await getMatchById(resolveAdapter(args), args.matchId);
|
|
3954
|
+
const { match, degraded: degraded2, source: source2 } = await getMatchById(resolveAdapter(args), args.matchId);
|
|
3937
3955
|
const matches = match ? [match] : [];
|
|
3938
3956
|
return shareResult(
|
|
3939
3957
|
"match",
|
|
@@ -3944,6 +3962,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3944
3962
|
matches,
|
|
3945
3963
|
marketSignals: await signalsFor(matches),
|
|
3946
3964
|
source: source2,
|
|
3965
|
+
degraded: degraded2,
|
|
3947
3966
|
emptyNote: `No match found with id ${args.matchId}.`,
|
|
3948
3967
|
installLine: `npx @claudinho/cli match ${args.matchId}`,
|
|
3949
3968
|
tz: args.tz,
|
|
@@ -3974,7 +3993,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3974
3993
|
);
|
|
3975
3994
|
}
|
|
3976
3995
|
const date = args.date ?? localDate((/* @__PURE__ */ new Date()).toISOString(), args.tz);
|
|
3977
|
-
const { matches: all, source } = await getMatchesForDate(resolveAdapter(args), date);
|
|
3996
|
+
const { matches: all, degraded, source } = await getMatchesForDate(resolveAdapter(args), date);
|
|
3978
3997
|
const todays = fixturesByDate(date, all, args.tz);
|
|
3979
3998
|
const human = formatDate(`${date}T12:00:00.000Z`, { tz: args.tz, locale: args.lang });
|
|
3980
3999
|
return shareResult(
|
|
@@ -3986,6 +4005,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3986
4005
|
matches: todays,
|
|
3987
4006
|
marketSignals: await signalsFor(todays),
|
|
3988
4007
|
source,
|
|
4008
|
+
degraded,
|
|
3989
4009
|
emptyNote: `No matches scheduled for ${human}.`,
|
|
3990
4010
|
installLine: "npx @claudinho/cli today",
|
|
3991
4011
|
tz: args.tz,
|
|
@@ -3997,7 +4017,7 @@ async function toolGetShareSnippet(args) {
|
|
|
3997
4017
|
|
|
3998
4018
|
// src/server.ts
|
|
3999
4019
|
var SERVER_NAME = "claudinho";
|
|
4000
|
-
var SERVER_VERSION = "0.5.
|
|
4020
|
+
var SERVER_VERSION = "0.5.2";
|
|
4001
4021
|
var VOICE = asFlavorLevel(process.env.CLAUDINHO_FLAVOR) === "off" ? "" : `
|
|
4002
4022
|
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.`;
|
|
4003
4023
|
var INSTRUCTIONS = `Claudinho serves live scores, fixtures, and group standings for the 2026 men's football tournament.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"mcpName": "io.github.arturogarrido/claudinho",
|
|
5
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.",
|
|
6
6
|
"type": "module",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"tsup": "^8.0.0",
|
|
55
55
|
"typescript": "^5.7.0",
|
|
56
56
|
"vitest": "^4.1.0",
|
|
57
|
-
"@claudinho/core": "0.5.
|
|
57
|
+
"@claudinho/core": "0.5.2"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsup",
|