@claudinho/mcp 0.8.17 → 0.8.18
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 +91 -2
- package/package.json +2 -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
|
@@ -2802,6 +2802,60 @@ function groups(fixtures = SCHEDULE) {
|
|
|
2802
2802
|
for (const m of fixtures) if (m.group) set.add(m.group);
|
|
2803
2803
|
return [...set].sort();
|
|
2804
2804
|
}
|
|
2805
|
+
function norm2(s) {
|
|
2806
|
+
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]/g, "");
|
|
2807
|
+
}
|
|
2808
|
+
function allTeams(fixtures = allFixtures()) {
|
|
2809
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2810
|
+
for (const m of fixtures) {
|
|
2811
|
+
if (m.stage !== "GROUP") continue;
|
|
2812
|
+
for (const t2 of [m.home, m.away]) {
|
|
2813
|
+
if (!/^[A-Z]{3}$/.test(t2.code) || t2.flag === "\u{1F3F3}\uFE0F") continue;
|
|
2814
|
+
if (!seen.has(t2.code)) seen.set(t2.code, { ...t2, group: m.group ?? void 0 });
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
2818
|
+
}
|
|
2819
|
+
var TEAM_ALIASES = {
|
|
2820
|
+
turkey: "TUR",
|
|
2821
|
+
holland: "NED",
|
|
2822
|
+
korea: "KOR",
|
|
2823
|
+
skorea: "KOR",
|
|
2824
|
+
korearepublic: "KOR",
|
|
2825
|
+
republicofkorea: "KOR",
|
|
2826
|
+
czechrepublic: "CZE",
|
|
2827
|
+
congo: "COD",
|
|
2828
|
+
drcongo: "COD",
|
|
2829
|
+
drc: "COD",
|
|
2830
|
+
democraticrepublicofcongo: "COD",
|
|
2831
|
+
democraticrepublicofthecongo: "COD",
|
|
2832
|
+
cotedivoire: "CIV",
|
|
2833
|
+
caboverde: "CPV",
|
|
2834
|
+
bosnia: "BIH",
|
|
2835
|
+
bosniaandherzegovina: "BIH",
|
|
2836
|
+
us: "USA",
|
|
2837
|
+
america: "USA",
|
|
2838
|
+
unitedstatesofamerica: "USA"
|
|
2839
|
+
};
|
|
2840
|
+
function lookupTeam(query, fixtures = allFixtures()) {
|
|
2841
|
+
const roster = allTeams(fixtures);
|
|
2842
|
+
const q = norm2(query);
|
|
2843
|
+
if (!q) return { query, team: null, matches: [] };
|
|
2844
|
+
const byCode = roster.find((t2) => norm2(t2.code) === q);
|
|
2845
|
+
if (byCode) return { query, team: byCode, matches: [byCode] };
|
|
2846
|
+
const byName = roster.find((t2) => norm2(t2.name) === q);
|
|
2847
|
+
if (byName) return { query, team: byName, matches: [byName] };
|
|
2848
|
+
const aliasCode = TEAM_ALIASES[q];
|
|
2849
|
+
if (aliasCode) {
|
|
2850
|
+
const t2 = roster.find((r) => r.code === aliasCode);
|
|
2851
|
+
if (t2) return { query, team: t2, matches: [t2] };
|
|
2852
|
+
}
|
|
2853
|
+
if (q.length < 3) return { query, team: null, matches: [] };
|
|
2854
|
+
const prefix = roster.filter((t2) => norm2(t2.name).startsWith(q));
|
|
2855
|
+
const substr = roster.filter((t2) => norm2(t2.name).includes(q) && !prefix.includes(t2));
|
|
2856
|
+
const matches = [...prefix, ...substr];
|
|
2857
|
+
return { query, team: matches.length === 1 ? matches[0] : null, matches };
|
|
2858
|
+
}
|
|
2805
2859
|
function blankRow(team) {
|
|
2806
2860
|
return {
|
|
2807
2861
|
team,
|
|
@@ -4884,6 +4938,19 @@ ${matchLine(fixture, opts)}`, source, args.lang),
|
|
|
4884
4938
|
data: { team: code, fixture, degraded }
|
|
4885
4939
|
};
|
|
4886
4940
|
}
|
|
4941
|
+
function toolGetTeam(args) {
|
|
4942
|
+
const { team, matches } = lookupTeam(args.query ?? "");
|
|
4943
|
+
const data = { query: args.query ?? "", team: team ?? null, matches, count: matches.length };
|
|
4944
|
+
let text;
|
|
4945
|
+
if (team) {
|
|
4946
|
+
text = `${team.code} \u2014 ${team.flag} ${team.name}${team.group ? ` \xB7 Group ${team.group}` : ""}`;
|
|
4947
|
+
} else if (matches.length > 0) {
|
|
4948
|
+
text = `"${args.query}" is ambiguous. Did you mean: ${matches.map((t2) => `${t2.name} (${t2.code})`).join(", ")}?`;
|
|
4949
|
+
} else {
|
|
4950
|
+
text = `No team found for "${args.query}". Use a nation name or 3-letter code (e.g. Mexico, MEX).`;
|
|
4951
|
+
}
|
|
4952
|
+
return { text: withDisclaimer(text), data };
|
|
4953
|
+
}
|
|
4887
4954
|
async function toolGetMarketSignal(args) {
|
|
4888
4955
|
const provider = resolveMarketProvider(args);
|
|
4889
4956
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
@@ -5152,11 +5219,12 @@ async function toolGetShareSnippet(args) {
|
|
|
5152
5219
|
|
|
5153
5220
|
// src/server.ts
|
|
5154
5221
|
var SERVER_NAME = "claudinho";
|
|
5155
|
-
var SERVER_VERSION = "0.8.
|
|
5222
|
+
var SERVER_VERSION = "0.8.18";
|
|
5156
5223
|
var VOICE = asFlavorLevel(process.env.CLAUDINHO_FLAVOR) === "off" ? "" : `
|
|
5157
5224
|
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
5225
|
var INSTRUCTIONS = `Claudinho serves live scores, fixtures, and group standings for the 2026 men's football tournament.
|
|
5159
|
-
|
|
5226
|
+
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.
|
|
5227
|
+
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
5228
|
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
5229
|
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
5230
|
${DISCLAIMER}`;
|
|
@@ -5240,6 +5308,13 @@ var shareOut = {
|
|
|
5240
5308
|
matches: z.array(matchOut).optional(),
|
|
5241
5309
|
marketSignals: z.record(anyObj).optional()
|
|
5242
5310
|
};
|
|
5311
|
+
var teamInfo = z.object({ code: z.string(), name: z.string(), flag: z.string(), group: z.string() }).partial().passthrough();
|
|
5312
|
+
var teamOut = {
|
|
5313
|
+
query: z.string(),
|
|
5314
|
+
team: teamInfo.nullable(),
|
|
5315
|
+
matches: z.array(teamInfo),
|
|
5316
|
+
count: z.number()
|
|
5317
|
+
};
|
|
5243
5318
|
function toContent(r) {
|
|
5244
5319
|
return {
|
|
5245
5320
|
content: [
|
|
@@ -5375,6 +5450,20 @@ function buildServer() {
|
|
|
5375
5450
|
},
|
|
5376
5451
|
async (args) => toContent(await toolGetShareSnippet(args))
|
|
5377
5452
|
);
|
|
5453
|
+
server.registerTool(
|
|
5454
|
+
"get_team",
|
|
5455
|
+
{
|
|
5456
|
+
title: "Resolve a team",
|
|
5457
|
+
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.`,
|
|
5458
|
+
inputSchema: {
|
|
5459
|
+
query: z.string().describe('Team name or 3-letter code, e.g. "Mexico", "MEX", "DR Congo"')
|
|
5460
|
+
},
|
|
5461
|
+
// Read-only AND offline — resolves against the bundled roster, no provider call.
|
|
5462
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
5463
|
+
outputSchema: teamOut
|
|
5464
|
+
},
|
|
5465
|
+
async (args) => toContent(toolGetTeam(args))
|
|
5466
|
+
);
|
|
5378
5467
|
server.registerResource(
|
|
5379
5468
|
"standings",
|
|
5380
5469
|
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.18",
|
|
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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"tsup": "^8.0.0",
|
|
58
58
|
"typescript": "^5.7.0",
|
|
59
59
|
"vitest": "^4.1.9",
|
|
60
|
-
"@claudinho/core": "0.8.
|
|
60
|
+
"@claudinho/core": "0.8.18"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsup",
|