cito-mcp 0.4.0 → 0.4.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/README.md +48 -1
- package/dist/tools/insight.js +126 -7
- package/dist/tools/leaderboard.js +18 -2
- package/dist/tools/match.js +75 -5
- package/dist/tools/meta.js +6 -6
- package/dist/tools/normalize.js +221 -15
- package/dist/tools/odds.js +122 -20
- package/dist/tools/player.js +88 -7
- package/dist/tools/rankings.js +75 -10
- package/dist/tools/resolve.js +121 -8
- package/dist/tools/schedule.js +101 -12
- package/dist/tools/standings.js +63 -10
- package/dist/tools/tournaments.js +70 -4
- package/package.json +1 -1
|
@@ -69,6 +69,9 @@ Do not use when: the draw bracket or a specific edition → event_card with the
|
|
|
69
69
|
|
|
70
70
|
Tennis-only. Levels accept names (Grand Slam, WTA 1000, ATP 500, Challenger,
|
|
71
71
|
ITF World Tennis Tour, Davis Cup, ...) or raw tier codes (G, M, A, C, D).
|
|
72
|
+
A level name that starts with ATP or WTA also fixes the tour (and conflicts with
|
|
73
|
+
a contradicting tour= argument are rejected), so "WTA 1000" cannot return ATP
|
|
74
|
+
Masters events even though the upstream filter is not tour-aware.
|
|
72
75
|
Note the source does not separate ATP 500 from ATP 250 — both are ATP Tour.
|
|
73
76
|
|
|
74
77
|
Parallel-safe: yes. Upstream cost: 1.`,
|
|
@@ -171,6 +174,44 @@ Parallel-safe: yes. Upstream cost: 1.`,
|
|
|
171
174
|
});
|
|
172
175
|
}
|
|
173
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* A level name that starts with a tour token constrains the tour, and the
|
|
179
|
+
* upstream filter does not know that.
|
|
180
|
+
*
|
|
181
|
+
* The API's reverse level map is not tour-aware: `_TIER_WTA` carries BOTH
|
|
182
|
+
* "PM" and "M" as "WTA 1000" (M is the fallback label for a WTA row stored
|
|
183
|
+
* with the ATP code), so `?level=WTA 1000` resolves to `tier_code IN
|
|
184
|
+
* ('PM','M')` and returns every ATP Masters 1000 event alongside the WTA
|
|
185
|
+
* ones. Verified live: asking for "WTA 1000" returned Rome Masters, Madrid
|
|
186
|
+
* Masters, Monte Carlo, Miami Masters and Indian Wells Masters (all ATP).
|
|
187
|
+
* The same widening hits "WTA 125" (code C, shared with ATP Challengers) and
|
|
188
|
+
* "ATP 500"/"ATP 250" (code A, shared with WTA tour-level rows).
|
|
189
|
+
*
|
|
190
|
+
* The tool therefore asserts the invariant the label states: it sends the
|
|
191
|
+
* tour, and it verifies the rows that come back. Rows that contradict the
|
|
192
|
+
* requested level are dropped and counted rather than passed through as
|
|
193
|
+
* "close enough" — a filter that returns other-tour events is worse than a
|
|
194
|
+
* filter that returns nothing, because the caller has no way to notice.
|
|
195
|
+
*/
|
|
196
|
+
const levelTourPrefix = (() => {
|
|
197
|
+
const m = /^(ATP|WTA)\b/i.exec(levelRaw);
|
|
198
|
+
return m ? m[1].toUpperCase() : null;
|
|
199
|
+
})();
|
|
200
|
+
if (levelTourPrefix && tourRaw && tourRaw !== levelTourPrefix) {
|
|
201
|
+
return errorEnvelope({
|
|
202
|
+
code: 'VALIDATION',
|
|
203
|
+
message: `tour ('${tourRaw}') contradicts level ('${levelRaw}'), which names the ${levelTourPrefix} tour`,
|
|
204
|
+
game,
|
|
205
|
+
source: 'tournaments',
|
|
206
|
+
requestId,
|
|
207
|
+
tookMs: Date.now() - started,
|
|
208
|
+
recover: [
|
|
209
|
+
`Drop tour, or set tour=${levelTourPrefix}`,
|
|
210
|
+
'A level name that starts with ATP/WTA already determines the tour',
|
|
211
|
+
],
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const effectiveTour = tourRaw || levelTourPrefix || '';
|
|
174
215
|
const limit = clampInt(args.limit, 20, 1, 50);
|
|
175
216
|
const page = clampInt(args.page, 1, 1, 10000);
|
|
176
217
|
const countryCode = typeof args.countryCode === 'string' && args.countryCode.trim()
|
|
@@ -182,7 +223,7 @@ Parallel-safe: yes. Upstream cost: 1.`,
|
|
|
182
223
|
limit,
|
|
183
224
|
page,
|
|
184
225
|
...(year !== null ? { year } : {}),
|
|
185
|
-
...(
|
|
226
|
+
...(effectiveTour ? { tour: effectiveTour } : {}),
|
|
186
227
|
...(levelRaw ? { level: levelRaw } : {}),
|
|
187
228
|
...(surface ? { surface } : {}),
|
|
188
229
|
...(countryCode ? { country_code: countryCode } : {}),
|
|
@@ -204,17 +245,35 @@ Parallel-safe: yes. Upstream cost: 1.`,
|
|
|
204
245
|
}
|
|
205
246
|
const root = asRecord(res.data) ?? {};
|
|
206
247
|
const body = asRecord(root.data) ?? root;
|
|
207
|
-
const
|
|
248
|
+
const rawItems = (Array.isArray(body.items) ? body.items : []).map(normalizeTournament);
|
|
249
|
+
// Verify the invariant the level label states. See levelTourPrefix above.
|
|
250
|
+
const contradictions = levelTourPrefix
|
|
251
|
+
? rawItems.filter((t) => t.tour && t.tour.toUpperCase() !== levelTourPrefix)
|
|
252
|
+
: [];
|
|
253
|
+
const items = contradictions.length
|
|
254
|
+
? rawItems.filter((t) => !t.tour || t.tour.toUpperCase() === levelTourPrefix)
|
|
255
|
+
: rawItems;
|
|
208
256
|
const total = typeof body.total === 'number' ? body.total : items.length;
|
|
209
257
|
const pageSize = typeof body.page_size === 'number' ? body.page_size : limit;
|
|
210
258
|
const filters = [
|
|
211
259
|
year !== null ? String(year) : null,
|
|
212
|
-
|
|
260
|
+
effectiveTour || null,
|
|
213
261
|
levelRaw || null,
|
|
214
262
|
surface || null,
|
|
215
263
|
countryCode || null,
|
|
216
264
|
q ? `"${q}"` : null,
|
|
217
265
|
].filter(Boolean);
|
|
266
|
+
const warnings = [];
|
|
267
|
+
if (contradictions.length) {
|
|
268
|
+
const sample = contradictions
|
|
269
|
+
.slice(0, 5)
|
|
270
|
+
.map((t) => `${t.id} (${t.tour})`)
|
|
271
|
+
.join(', ');
|
|
272
|
+
warnings.push(`The upstream level filter returned ${contradictions.length} tournament(s) that contradict level="${levelRaw}": ` +
|
|
273
|
+
`${sample}. They were dropped. The API's level reverse-map is not tour-aware (WTA 1000 resolves to tier codes ` +
|
|
274
|
+
`PM and M), which is a server-side defect; this tool sends tour=${levelTourPrefix} as well, so a repeat of this ` +
|
|
275
|
+
`warning means the upstream filter is widening again.`);
|
|
276
|
+
}
|
|
218
277
|
return successEnvelope({
|
|
219
278
|
pagination: {
|
|
220
279
|
limit: pageSize,
|
|
@@ -230,18 +289,25 @@ Parallel-safe: yes. Upstream cost: 1.`,
|
|
|
230
289
|
tookMs: Date.now() - started,
|
|
231
290
|
upstreamCalls: 1,
|
|
232
291
|
rateLimit: res.headers,
|
|
292
|
+
warnings: warnings.length ? warnings : undefined,
|
|
233
293
|
data: {
|
|
234
294
|
title: filters.length > 0
|
|
235
295
|
? `Tennis tournaments — ${filters.join(' ')}`
|
|
236
296
|
: 'Tennis tournaments',
|
|
237
297
|
filters: {
|
|
238
298
|
year,
|
|
239
|
-
tour:
|
|
299
|
+
tour: effectiveTour || null,
|
|
240
300
|
level: levelRaw || null,
|
|
241
301
|
surface: surface ?? null,
|
|
242
302
|
countryCode: countryCode || null,
|
|
243
303
|
q: q || null,
|
|
244
304
|
},
|
|
305
|
+
levelFilterIntegrity: {
|
|
306
|
+
levelNamesTour: levelTourPrefix,
|
|
307
|
+
rowsChecked: rawItems.length,
|
|
308
|
+
contradictionsDropped: contradictions.length,
|
|
309
|
+
ok: contradictions.length === 0,
|
|
310
|
+
},
|
|
245
311
|
items,
|
|
246
312
|
total,
|
|
247
313
|
page,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cito-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Standalone MCP server for the Cito esports and sports API — 42 curated outcome tools for agents (live scoreboards, round economy, opening duels, clutches, vetoes, rosters, tennis, mma).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|