@claudinho/mcp 0.8.17 → 0.8.19
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 +4 -2
- package/dist/index.js +172 -22
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -51,9 +51,11 @@ Then just ask your agent naturally — it picks the right tool and answers with
|
|
|
51
51
|
| `get_next_fixture` | a team's next match (3-letter code, e.g. `MEX`) — live-resolves a confirmed knockout tie from the feed; group fixtures offline, fails back to the bundled schedule if the feed is down |
|
|
52
52
|
| `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 |
|
|
53
53
|
| `get_share_snippet` | a copy-pasteable plain-text card — for a match, a team's next fixture, a group's standings table (`group`), the knockout bracket (`bracket: true`, optional `knockoutStage`), a date, or live — hand the returned snippet to the user as-is |
|
|
54
|
+
| `get_team` | resolve a nation name or code to its FIFA 3-letter code, flag, and group — fuzzy (`Mexico`, `mex`, `DR Congo`, `Türkiye`); call it first to turn a user's team name into the code the other tools need. Offline (no network) |
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
(`en`/`es`/`pt`/`fr`), and `flavor` (`off`/`subtle`/`full`)
|
|
56
|
+
Most tools are **read-only** (`readOnlyHint`) and accept optional `tz`, `lang`
|
|
57
|
+
(`en`/`es`/`pt`/`fr`), and `flavor` (`off`/`subtle`/`full`); `get_team` is read-only
|
|
58
|
+
**and** offline. Every response carries
|
|
57
59
|
human-readable text **and** structured content, validated against each tool's
|
|
58
60
|
declared `outputSchema`.
|
|
59
61
|
|
package/dist/index.js
CHANGED
|
@@ -504,6 +504,33 @@ function shiftUtcDate(dateISO, days) {
|
|
|
504
504
|
const [y, m, d] = dateISO.slice(0, 10).split("-").map(Number);
|
|
505
505
|
return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
|
|
506
506
|
}
|
|
507
|
+
var FEED_TEXT_MAX = 100;
|
|
508
|
+
function sanitizeFeedText(value, max = FEED_TEXT_MAX) {
|
|
509
|
+
let out = "";
|
|
510
|
+
let count = 0;
|
|
511
|
+
for (const ch of String(value)) {
|
|
512
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
513
|
+
const isWhitespaceControl = cp === 9 || cp === 10 || cp === 13;
|
|
514
|
+
if ((cp <= 31 || cp >= 127 && cp <= 159) && !isWhitespaceControl) continue;
|
|
515
|
+
if (count >= max) break;
|
|
516
|
+
out += isWhitespaceControl ? " " : ch;
|
|
517
|
+
count++;
|
|
518
|
+
}
|
|
519
|
+
return out;
|
|
520
|
+
}
|
|
521
|
+
var segmenter = new Intl.Segmenter();
|
|
522
|
+
var WIDE_CLUSTER = new RegExp("^(?:\\p{Regional_Indicator}|\\p{Extended_Pictographic})", "u");
|
|
523
|
+
function displayWidth(s) {
|
|
524
|
+
let w = 0;
|
|
525
|
+
for (const { segment } of segmenter.segment(s)) {
|
|
526
|
+
w += WIDE_CLUSTER.test(segment) ? 2 : 1;
|
|
527
|
+
}
|
|
528
|
+
return w;
|
|
529
|
+
}
|
|
530
|
+
function padVisible(s, width) {
|
|
531
|
+
const w = displayWidth(s);
|
|
532
|
+
return w >= width ? s : s + " ".repeat(width - w);
|
|
533
|
+
}
|
|
507
534
|
function outcomeFromScore(home, away) {
|
|
508
535
|
if (home > away) return "H";
|
|
509
536
|
if (home < away) return "A";
|
|
@@ -2802,6 +2829,60 @@ function groups(fixtures = SCHEDULE) {
|
|
|
2802
2829
|
for (const m of fixtures) if (m.group) set.add(m.group);
|
|
2803
2830
|
return [...set].sort();
|
|
2804
2831
|
}
|
|
2832
|
+
function norm2(s) {
|
|
2833
|
+
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]/g, "");
|
|
2834
|
+
}
|
|
2835
|
+
function allTeams(fixtures = allFixtures()) {
|
|
2836
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2837
|
+
for (const m of fixtures) {
|
|
2838
|
+
if (m.stage !== "GROUP") continue;
|
|
2839
|
+
for (const t2 of [m.home, m.away]) {
|
|
2840
|
+
if (!/^[A-Z]{3}$/.test(t2.code) || t2.flag === "\u{1F3F3}\uFE0F") continue;
|
|
2841
|
+
if (!seen.has(t2.code)) seen.set(t2.code, { ...t2, group: m.group ?? void 0 });
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
2845
|
+
}
|
|
2846
|
+
var TEAM_ALIASES = {
|
|
2847
|
+
turkey: "TUR",
|
|
2848
|
+
holland: "NED",
|
|
2849
|
+
korea: "KOR",
|
|
2850
|
+
skorea: "KOR",
|
|
2851
|
+
korearepublic: "KOR",
|
|
2852
|
+
republicofkorea: "KOR",
|
|
2853
|
+
czechrepublic: "CZE",
|
|
2854
|
+
congo: "COD",
|
|
2855
|
+
drcongo: "COD",
|
|
2856
|
+
drc: "COD",
|
|
2857
|
+
democraticrepublicofcongo: "COD",
|
|
2858
|
+
democraticrepublicofthecongo: "COD",
|
|
2859
|
+
cotedivoire: "CIV",
|
|
2860
|
+
caboverde: "CPV",
|
|
2861
|
+
bosnia: "BIH",
|
|
2862
|
+
bosniaandherzegovina: "BIH",
|
|
2863
|
+
us: "USA",
|
|
2864
|
+
america: "USA",
|
|
2865
|
+
unitedstatesofamerica: "USA"
|
|
2866
|
+
};
|
|
2867
|
+
function lookupTeam(query, fixtures = allFixtures()) {
|
|
2868
|
+
const roster = allTeams(fixtures);
|
|
2869
|
+
const q = norm2(query);
|
|
2870
|
+
if (!q) return { query, team: null, matches: [] };
|
|
2871
|
+
const byCode = roster.find((t2) => norm2(t2.code) === q);
|
|
2872
|
+
if (byCode) return { query, team: byCode, matches: [byCode] };
|
|
2873
|
+
const byName = roster.find((t2) => norm2(t2.name) === q);
|
|
2874
|
+
if (byName) return { query, team: byName, matches: [byName] };
|
|
2875
|
+
const aliasCode = TEAM_ALIASES[q];
|
|
2876
|
+
if (aliasCode) {
|
|
2877
|
+
const t2 = roster.find((r) => r.code === aliasCode);
|
|
2878
|
+
if (t2) return { query, team: t2, matches: [t2] };
|
|
2879
|
+
}
|
|
2880
|
+
if (q.length < 3) return { query, team: null, matches: [] };
|
|
2881
|
+
const prefix = roster.filter((t2) => norm2(t2.name).startsWith(q));
|
|
2882
|
+
const substr = roster.filter((t2) => norm2(t2.name).includes(q) && !prefix.includes(t2));
|
|
2883
|
+
const matches = [...prefix, ...substr];
|
|
2884
|
+
return { query, team: matches.length === 1 ? matches[0] : null, matches };
|
|
2885
|
+
}
|
|
2805
2886
|
function blankRow(team) {
|
|
2806
2887
|
return {
|
|
2807
2888
|
team,
|
|
@@ -2827,6 +2908,7 @@ var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
|
|
|
2827
2908
|
var DEFAULT_COMPETITION = "fifa.world";
|
|
2828
2909
|
var DEFAULT_BASE = `${ESPN_SOCCER}/${DEFAULT_COMPETITION}`;
|
|
2829
2910
|
var USER_AGENT = "claudinho/0.0 (+https://github.com/arturogarrido/claudinho)";
|
|
2911
|
+
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
2830
2912
|
function competitionBase(slug) {
|
|
2831
2913
|
return `${ESPN_SOCCER}/${slug}`;
|
|
2832
2914
|
}
|
|
@@ -2870,9 +2952,15 @@ function toInt(s) {
|
|
|
2870
2952
|
return Number.isFinite(n) ? n : void 0;
|
|
2871
2953
|
}
|
|
2872
2954
|
function toTeam(t2) {
|
|
2873
|
-
const name =
|
|
2874
|
-
|
|
2875
|
-
|
|
2955
|
+
const name = sanitizeFeedText(
|
|
2956
|
+
t2?.displayName ?? t2?.name ?? t2?.location ?? t2?.shortDisplayName ?? "TBD"
|
|
2957
|
+
);
|
|
2958
|
+
const code = sanitizeFeedText(t2?.abbreviation ?? name.slice(0, 3)).toUpperCase();
|
|
2959
|
+
return {
|
|
2960
|
+
code,
|
|
2961
|
+
name,
|
|
2962
|
+
flag: nationToFlag(sanitizeFeedText(t2?.displayName ?? t2?.abbreviation ?? name))
|
|
2963
|
+
};
|
|
2876
2964
|
}
|
|
2877
2965
|
function mapEspnEvent(ev, ctx = {}) {
|
|
2878
2966
|
const comp = ev.competitions?.[0];
|
|
@@ -2903,9 +2991,9 @@ function mapEspnEvent(ev, ctx = {}) {
|
|
|
2903
2991
|
stage,
|
|
2904
2992
|
group,
|
|
2905
2993
|
kickoff: ev.date,
|
|
2906
|
-
venue: comp?.venue?.fullName ?? "",
|
|
2907
|
-
city: comp?.venue?.address?.city || void 0,
|
|
2908
|
-
country: comp?.venue?.address?.country || void 0,
|
|
2994
|
+
venue: sanitizeFeedText(comp?.venue?.fullName ?? ""),
|
|
2995
|
+
city: sanitizeFeedText(comp?.venue?.address?.city ?? "") || void 0,
|
|
2996
|
+
country: sanitizeFeedText(comp?.venue?.address?.country ?? "") || void 0,
|
|
2909
2997
|
home,
|
|
2910
2998
|
away,
|
|
2911
2999
|
score: hasScore ? { home: hs, away: as } : void 0,
|
|
@@ -3029,11 +3117,18 @@ var EspnAdapter = class {
|
|
|
3029
3117
|
try {
|
|
3030
3118
|
const res = await doFetch(url, {
|
|
3031
3119
|
signal: controller.signal,
|
|
3120
|
+
// ESPN never legitimately redirects; following one would sidestep the
|
|
3121
|
+
// fixed-host guarantee, so treat any redirect as a failure (degraded).
|
|
3122
|
+
redirect: "error",
|
|
3032
3123
|
headers: { Accept: "application/json", "User-Agent": USER_AGENT }
|
|
3033
3124
|
});
|
|
3034
3125
|
if (!res.ok) {
|
|
3035
3126
|
throw new Error(`ESPN request failed: ${res.status} ${res.statusText}`);
|
|
3036
3127
|
}
|
|
3128
|
+
const length = Number(res.headers?.get?.("content-length"));
|
|
3129
|
+
if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
|
|
3130
|
+
throw new Error(`ESPN response too large: ${length} bytes`);
|
|
3131
|
+
}
|
|
3037
3132
|
return await res.json();
|
|
3038
3133
|
} finally {
|
|
3039
3134
|
clearTimeout(timer);
|
|
@@ -4175,12 +4270,19 @@ var PolymarketProvider = class {
|
|
|
4175
4270
|
const doFetch = this.opts.fetchImpl ?? fetch;
|
|
4176
4271
|
const res = await doFetch(url, {
|
|
4177
4272
|
signal: AbortSignal.timeout(timeoutMs ?? this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
4273
|
+
// Gamma never legitimately redirects; following one would sidestep the
|
|
4274
|
+
// host allow-list (it only validates the base URL), so reject redirects.
|
|
4275
|
+
redirect: "error",
|
|
4178
4276
|
headers: { Accept: "application/json", "User-Agent": USER_AGENT2 }
|
|
4179
4277
|
});
|
|
4180
4278
|
if (res.status === 404) return void 0;
|
|
4181
4279
|
if (!res.ok) {
|
|
4182
4280
|
throw new Error(`Polymarket request failed: ${res.status} ${res.statusText}`);
|
|
4183
4281
|
}
|
|
4282
|
+
const length = Number(res.headers?.get?.("content-length"));
|
|
4283
|
+
if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
|
|
4284
|
+
throw new Error(`Polymarket response too large: ${length} bytes`);
|
|
4285
|
+
}
|
|
4184
4286
|
const data = await res.json();
|
|
4185
4287
|
const event = Array.isArray(data) ? data[0] : data;
|
|
4186
4288
|
return event && typeof event === "object" ? event : void 0;
|
|
@@ -4623,17 +4725,21 @@ function matchList(matches, empty, opts = {}) {
|
|
|
4623
4725
|
}
|
|
4624
4726
|
function standingsTable(group, rows) {
|
|
4625
4727
|
const header = `Group ${group}`;
|
|
4626
|
-
const
|
|
4627
|
-
const
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4728
|
+
const line = (team, p, w, d, l, gd2, pts) => `${padVisible(team, 24)} ${p.padStart(2)} ${w.padStart(2)} ${d.padStart(2)} ${l.padStart(2)} ${gd2.padStart(3)} ${pts.padStart(3)}`;
|
|
4729
|
+
const cols = line("Team", "P", "W", "D", "L", "GD", "Pts");
|
|
4730
|
+
const lines = rows.map(
|
|
4731
|
+
(r) => line(
|
|
4732
|
+
`${r.team.flag} ${r.team.name}`,
|
|
4733
|
+
String(r.played),
|
|
4734
|
+
String(r.won),
|
|
4735
|
+
String(r.drawn),
|
|
4736
|
+
String(r.lost),
|
|
4737
|
+
r.goalDiff > 0 ? `+${r.goalDiff}` : String(r.goalDiff),
|
|
4738
|
+
String(r.points)
|
|
4739
|
+
)
|
|
4740
|
+
);
|
|
4632
4741
|
return [header, cols, ...lines].join("\n");
|
|
4633
4742
|
}
|
|
4634
|
-
function pad(n) {
|
|
4635
|
-
return `${n}`.padStart(2);
|
|
4636
|
-
}
|
|
4637
4743
|
var DISCLAIMER = "Claudinho is an independent fan project \u2014 not affiliated with or endorsed by FIFA or Anthropic.";
|
|
4638
4744
|
|
|
4639
4745
|
// src/tools.ts
|
|
@@ -4874,16 +4980,31 @@ async function toolGetNextFixture(args) {
|
|
|
4874
4980
|
const msg = degraded ? `Couldn't reach the data provider \u2014 no upcoming fixture confirmed for ${code}.` : `No upcoming fixture found for ${code}.`;
|
|
4875
4981
|
return {
|
|
4876
4982
|
text: withDisclaimer(msg, void 0, args.lang),
|
|
4877
|
-
data: { team: code, fixture: null, degraded }
|
|
4983
|
+
data: { team: code, fixture: null, degraded, source: source ?? null }
|
|
4878
4984
|
};
|
|
4879
4985
|
}
|
|
4880
4986
|
const opts = fmtOpts(args);
|
|
4881
4987
|
return {
|
|
4988
|
+
// `source` in data mirrors the text's "Live data: …" attribution (parity
|
|
4989
|
+
// with CLI `next --json`); null for a static group fixture (no live source).
|
|
4882
4990
|
text: withDisclaimer(`Next up for ${code}:
|
|
4883
4991
|
${matchLine(fixture, opts)}`, source, args.lang),
|
|
4884
|
-
data: { team: code, fixture, degraded }
|
|
4992
|
+
data: { team: code, fixture, degraded, source: source ?? null }
|
|
4885
4993
|
};
|
|
4886
4994
|
}
|
|
4995
|
+
function toolGetTeam(args) {
|
|
4996
|
+
const { team, matches } = lookupTeam(args.query ?? "");
|
|
4997
|
+
const data = { query: args.query ?? "", team: team ?? null, matches, count: matches.length };
|
|
4998
|
+
let text;
|
|
4999
|
+
if (team) {
|
|
5000
|
+
text = `${team.code} \u2014 ${team.flag} ${team.name}${team.group ? ` \xB7 Group ${team.group}` : ""}`;
|
|
5001
|
+
} else if (matches.length > 0) {
|
|
5002
|
+
text = `"${args.query}" is ambiguous. Did you mean: ${matches.map((t2) => `${t2.name} (${t2.code})`).join(", ")}?`;
|
|
5003
|
+
} else {
|
|
5004
|
+
text = `No team found for "${args.query}". Use a nation name or 3-letter code (e.g. Mexico, MEX).`;
|
|
5005
|
+
}
|
|
5006
|
+
return { text: withDisclaimer(text), data };
|
|
5007
|
+
}
|
|
4887
5008
|
async function toolGetMarketSignal(args) {
|
|
4888
5009
|
const provider = resolveMarketProvider(args);
|
|
4889
5010
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
@@ -5152,11 +5273,12 @@ async function toolGetShareSnippet(args) {
|
|
|
5152
5273
|
|
|
5153
5274
|
// src/server.ts
|
|
5154
5275
|
var SERVER_NAME = "claudinho";
|
|
5155
|
-
var SERVER_VERSION = "0.8.
|
|
5276
|
+
var SERVER_VERSION = "0.8.19";
|
|
5156
5277
|
var VOICE = asFlavorLevel(process.env.CLAUDINHO_FLAVOR) === "off" ? "" : `
|
|
5157
5278
|
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.`;
|
|
5158
5279
|
var INSTRUCTIONS = `Claudinho serves live scores, fixtures, and group standings for the 2026 men's football tournament.
|
|
5159
|
-
|
|
5280
|
+
The team-taking tools (get_next_fixture, get_market_signal, get_share_snippet) expect a 3-letter code (e.g. MEX). When the user gives a nation NAME, call get_team FIRST to resolve it \u2014 get_team is fuzzy ("Mexico", "DR Congo", "T\xFCrkiye"), offline, and returns candidates when the name is ambiguous.
|
|
5281
|
+
Use get_live during matches, get_today for a day's schedule, get_next_fixture for a specific team, get_standings for group tables, and get_bracket for the knockout tree.
|
|
5160
5282
|
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.
|
|
5161
5283
|
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}
|
|
5162
5284
|
${DISCLAIMER}`;
|
|
@@ -5166,7 +5288,9 @@ var teamArg = z.string().regex(/^[A-Za-z]{3}$/, "a 3-letter team code, e.g. MEX"
|
|
|
5166
5288
|
var flavorArg = z.enum(["off", "subtle", "full"]);
|
|
5167
5289
|
var commonArgs = {
|
|
5168
5290
|
tz: z.string().optional().describe("IANA timezone for kickoff times, e.g. America/Mexico_City"),
|
|
5169
|
-
lang: z.string().optional().describe(
|
|
5291
|
+
lang: z.string().optional().describe(
|
|
5292
|
+
"Locale for dates, provider attribution, and commentary: en, es, pt, fr (the summary scaffold stays English; other locales fall back to en)"
|
|
5293
|
+
),
|
|
5170
5294
|
flavor: flavorArg.optional().describe("Commentary flair: off, subtle, full (default: full)")
|
|
5171
5295
|
};
|
|
5172
5296
|
var teamRef = z.object({ code: z.string(), name: z.string(), flag: z.string() }).partial().passthrough();
|
|
@@ -5213,7 +5337,12 @@ var bracketOut = {
|
|
|
5213
5337
|
standingsDegraded: z.boolean().optional(),
|
|
5214
5338
|
source: src.optional()
|
|
5215
5339
|
};
|
|
5216
|
-
var nextOut = {
|
|
5340
|
+
var nextOut = {
|
|
5341
|
+
team: z.string(),
|
|
5342
|
+
fixture: matchOut.nullable(),
|
|
5343
|
+
degraded: z.boolean(),
|
|
5344
|
+
source: src
|
|
5345
|
+
};
|
|
5217
5346
|
var marketOut = {
|
|
5218
5347
|
matchId: z.string().nullable().optional(),
|
|
5219
5348
|
team: z.string().optional(),
|
|
@@ -5240,6 +5369,13 @@ var shareOut = {
|
|
|
5240
5369
|
matches: z.array(matchOut).optional(),
|
|
5241
5370
|
marketSignals: z.record(anyObj).optional()
|
|
5242
5371
|
};
|
|
5372
|
+
var teamInfo = z.object({ code: z.string(), name: z.string(), flag: z.string(), group: z.string() }).partial().passthrough();
|
|
5373
|
+
var teamOut = {
|
|
5374
|
+
query: z.string(),
|
|
5375
|
+
team: teamInfo.nullable(),
|
|
5376
|
+
matches: z.array(teamInfo),
|
|
5377
|
+
count: z.number()
|
|
5378
|
+
};
|
|
5243
5379
|
function toContent(r) {
|
|
5244
5380
|
return {
|
|
5245
5381
|
content: [
|
|
@@ -5258,7 +5394,7 @@ function buildServer() {
|
|
|
5258
5394
|
"get_today",
|
|
5259
5395
|
{
|
|
5260
5396
|
title: "Today's matches",
|
|
5261
|
-
description: "All fixtures for a date (default: today), with live score and minute overlaid on any match in play. Use this for a whole day's card; for only in-play matches use get_live, for one team's match use get_next_fixture, for a single match's detail use get_match. Kickoffs render in tz; lang localizes
|
|
5397
|
+
description: "All fixtures for a date (default: today), with live score and minute overlaid on any match in play. Use this for a whole day's card; for only in-play matches use get_live, for one team's match use get_next_fixture, for a single match's detail use get_match. Kickoffs render in tz; lang localizes dates, attribution, and commentary (en/es/pt/fr); flavor sets commentary tone.",
|
|
5262
5398
|
inputSchema: {
|
|
5263
5399
|
date: dateArg.optional().describe("Date as YYYY-MM-DD (default: today)"),
|
|
5264
5400
|
...commonArgs
|
|
@@ -5375,6 +5511,20 @@ function buildServer() {
|
|
|
5375
5511
|
},
|
|
5376
5512
|
async (args) => toContent(await toolGetShareSnippet(args))
|
|
5377
5513
|
);
|
|
5514
|
+
server.registerTool(
|
|
5515
|
+
"get_team",
|
|
5516
|
+
{
|
|
5517
|
+
title: "Resolve a team",
|
|
5518
|
+
description: `Resolve a nation name or 3-letter code to its FIFA code, flag, and group. Fuzzy and forgiving: accepts "Mexico", "mex", "USA", "DR Congo", "T\xFCrkiye"/"Turkey", "Holland", etc. Use this FIRST to turn a user's team name into the code the other tools need (get_next_fixture, get_standings, get_market_signal, get_share_snippet). Returns the single confident match (team), plus candidates (matches) when the query is ambiguous (e.g. "south" \u2192 South Africa, South Korea). Offline \u2014 reads the bundled roster, never the network.`,
|
|
5519
|
+
inputSchema: {
|
|
5520
|
+
query: z.string().describe('Team name or 3-letter code, e.g. "Mexico", "MEX", "DR Congo"')
|
|
5521
|
+
},
|
|
5522
|
+
// Read-only AND offline — resolves against the bundled roster, no provider call.
|
|
5523
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
5524
|
+
outputSchema: teamOut
|
|
5525
|
+
},
|
|
5526
|
+
async (args) => toContent(toolGetTeam(args))
|
|
5527
|
+
);
|
|
5378
5528
|
server.registerResource(
|
|
5379
5529
|
"standings",
|
|
5380
5530
|
new ResourceTemplate("standings://{group}", { list: void 0 }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.19",
|
|
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",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
+
"!dist/*.mcpb",
|
|
29
30
|
"README.md",
|
|
30
31
|
"LICENSE"
|
|
31
32
|
],
|
|
@@ -57,7 +58,7 @@
|
|
|
57
58
|
"tsup": "^8.0.0",
|
|
58
59
|
"typescript": "^5.7.0",
|
|
59
60
|
"vitest": "^4.1.9",
|
|
60
|
-
"@claudinho/core": "0.8.
|
|
61
|
+
"@claudinho/core": "0.8.19"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
63
64
|
"build": "tsup",
|