@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.
Files changed (2) hide show
  1. package/dist/index.js +36 -16
  2. 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 === "LIVE" || m.status === "HT");
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
- return { matches: await adapter.fetchLive(), degraded: false, source: adapter.name };
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
- const text = `Matches on ${date}:
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
- const text = marketSignal ? `${base}
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
- emptyNote: "No matches in play right now.",
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: degraded ? void 0 : source2,
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: degraded ? null : source2 ?? null,
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.0";
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.0",
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.0"
57
+ "@claudinho/core": "0.5.2"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsup",