cito-mcp 0.3.10 → 0.3.11
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/tools/live.js +36 -5
- package/dist/tools/match.js +5 -1
- package/dist/tools/team.js +27 -2
- package/package.json +1 -1
package/dist/tools/live.js
CHANGED
|
@@ -426,8 +426,31 @@ Example: { "game": "lol", "hours": 72, "team": "t1", "limit": 20 }`,
|
|
|
426
426
|
// also moves paging client-side, because an upstream offset would be
|
|
427
427
|
// counted against rows we are about to discard.
|
|
428
428
|
let clientTeamFilter = false;
|
|
429
|
+
// Set when this tool, not the upstream, is responsible for paging.
|
|
430
|
+
let clientDatePaging = false;
|
|
429
431
|
let query = { limit, offset };
|
|
430
|
-
if (game === 'lol') {
|
|
432
|
+
lolBranch: if (game === 'lol') {
|
|
433
|
+
// from/to means a date WINDOW, which /lol/schedule/upcoming cannot serve:
|
|
434
|
+
// it only knows `hours` forward from now, so asking for last month
|
|
435
|
+
// silently returned next week's fixtures with an "ignored filter"
|
|
436
|
+
// warning. /lol/schedule does accept from/to, so route there instead and
|
|
437
|
+
// let callers list completed matches.
|
|
438
|
+
if (from || to) {
|
|
439
|
+
path = '/lol/schedule';
|
|
440
|
+
clientDatePaging = true;
|
|
441
|
+
query = {
|
|
442
|
+
limit: String(Math.min(200, (offset + limit) * 4)),
|
|
443
|
+
...(from ? { from } : {}),
|
|
444
|
+
...(to ? { to } : {}),
|
|
445
|
+
...(league ? { leagueSlug: league } : {}),
|
|
446
|
+
...(team ? { teamSlug: team } : {}),
|
|
447
|
+
};
|
|
448
|
+
if (team)
|
|
449
|
+
clientTeamFilter = true;
|
|
450
|
+
if (tournamentId)
|
|
451
|
+
noteIgnored('tournamentId', 'use league filter or standings for LoL tournaments');
|
|
452
|
+
break lolBranch;
|
|
453
|
+
}
|
|
431
454
|
path = '/lol/schedule/upcoming';
|
|
432
455
|
// The upstream accepts teamSlug but does not apply it: asking for t1
|
|
433
456
|
// returned Dplus KIA, Team WE and NightBirds. That is a silently wrong
|
|
@@ -437,10 +460,13 @@ Example: { "game": "lol", "hours": 72, "team": "t1", "limit": 20 }`,
|
|
|
437
460
|
// here instead.
|
|
438
461
|
if (team)
|
|
439
462
|
clientTeamFilter = true;
|
|
463
|
+
// The upstream accepts `offset` and ignores it, so page two came back
|
|
464
|
+
// byte for byte identical to page one while the cursor advanced in the
|
|
465
|
+
// metadata. Over-fetch and slice here instead of trusting it.
|
|
466
|
+
clientDatePaging = true;
|
|
440
467
|
query = {
|
|
441
468
|
hours: String(hours),
|
|
442
|
-
limit: String(
|
|
443
|
-
...(clientTeamFilter ? {} : { offset: String(offset) }),
|
|
469
|
+
limit: String(Math.min(200, (offset + limit) * 4)),
|
|
444
470
|
...(league ? { leagueSlug: league } : {}),
|
|
445
471
|
...(team ? { teamSlug: team } : {}),
|
|
446
472
|
};
|
|
@@ -664,8 +690,13 @@ Example: { "game": "lol", "hours": 72, "team": "t1", "limit": 20 }`,
|
|
|
664
690
|
if (team && beforeTeamFilter > 0 && rows.length === 0) {
|
|
665
691
|
warnings.push(`team "${team}" matched 0 of ${beforeTeamFilter} upcoming fixtures in this window — widen hours or check the slug`);
|
|
666
692
|
}
|
|
667
|
-
const
|
|
668
|
-
|
|
693
|
+
const pageHere = clientTeamFilter || clientDatePaging;
|
|
694
|
+
const windowed = pageHere
|
|
695
|
+
? rows.slice(Number(offset), Number(offset) + Number(limit))
|
|
696
|
+
: rows.slice(0, limit);
|
|
697
|
+
// A date window can contain finished matches, so do not force "upcoming"
|
|
698
|
+
// onto rows that already carry a real state.
|
|
699
|
+
const items = windowed.map((row) => from || to ? normalizeMatch(game, row) : normalizeMatch(game, row, 'upcoming'));
|
|
669
700
|
const obj = asRecord(res.data);
|
|
670
701
|
const total = typeof obj?.total === 'number' ? obj.total : null;
|
|
671
702
|
const hasMore = (typeof obj?.hasMore === 'boolean' ? obj.hasMore : items.length >= limit) ||
|
package/dist/tools/match.js
CHANGED
|
@@ -481,7 +481,11 @@ Example: { "game": "lol", "matchId": "lol-match-1", "includeTimeline": true, "in
|
|
|
481
481
|
media: null,
|
|
482
482
|
advanced: null,
|
|
483
483
|
};
|
|
484
|
-
// base is
|
|
484
|
+
// base is ALWAYS fetched, whatever sections were asked for. Requesting
|
|
485
|
+
// sections:["timeline"] returned match:null, so a caller got a timeline with
|
|
486
|
+
// nothing to attach it to and no way to tell which match it belonged to.
|
|
487
|
+
// The header is the identity of the response, not an optional section.
|
|
488
|
+
wanted.add('base');
|
|
485
489
|
if (wanted.has('base')) {
|
|
486
490
|
const res = await getSection(ctx, primaryPath(game, matchId));
|
|
487
491
|
upstreamCalls += 1;
|
package/dist/tools/team.js
CHANGED
|
@@ -8,8 +8,15 @@ import { boolSchema, gameSchema, isPrimaryGame, limitSchema, parseGame, stringSc
|
|
|
8
8
|
function teamIdentity(game, raw, idHint, slugHint) {
|
|
9
9
|
const r = asRecord(raw) ?? {};
|
|
10
10
|
const nested = asRecord(r.data) ?? r;
|
|
11
|
-
const id = pickString(nested.id, nested.teamId, idHint, slugHint) ?? 'unknown';
|
|
12
11
|
const slug = pickString(nested.slug, nested.orgSlug, slugHint);
|
|
12
|
+
// When the caller asks for an alias the API resolves it, and the resolved
|
|
13
|
+
// slug is the truth. Falling back to the requested value for `id` produced
|
|
14
|
+
// id:"t1a" beside slug:"t1-challengers", a row that disagreed with itself.
|
|
15
|
+
const resolved = pickString(nested.slug, nested.orgSlug);
|
|
16
|
+
const id = pickString(nested.id, nested.teamId) ??
|
|
17
|
+
resolved ??
|
|
18
|
+
pickString(idHint, slugHint) ??
|
|
19
|
+
'unknown';
|
|
13
20
|
const name = pickString(nested.name, nested.tag, nested.code, slug, id) ?? id;
|
|
14
21
|
return {
|
|
15
22
|
id,
|
|
@@ -301,8 +308,26 @@ Example: { "game": "lol", "slug": "t1", "recentLimit": 10 }`,
|
|
|
301
308
|
const t = Date.parse(String(row?.startTime ?? ''));
|
|
302
309
|
return Number.isFinite(t) ? t : -Infinity;
|
|
303
310
|
};
|
|
311
|
+
// RECENT means finished AND in the past. The index also carries
|
|
312
|
+
// scheduled fixtures, and this path forced every row to
|
|
313
|
+
// "completed", so Gen.G and HLE both listed their 2026-09-05
|
|
314
|
+
// playoff match as a completed result days before it was played.
|
|
315
|
+
const nowMs = Date.now();
|
|
316
|
+
const isFinished = (row) => {
|
|
317
|
+
const r = asRecord(row) ?? {};
|
|
318
|
+
const t = Date.parse(String(r.startTime ?? ''));
|
|
319
|
+
if (Number.isFinite(t) && t > nowMs)
|
|
320
|
+
return false;
|
|
321
|
+
const state = String(r.state ?? r.status ?? '').toLowerCase();
|
|
322
|
+
if (/unstarted|scheduled|upcoming|not_?started|in_?progress|live/.test(state))
|
|
323
|
+
return false;
|
|
324
|
+
// No usable state: fall back to the clock plus a decided result.
|
|
325
|
+
if (!state)
|
|
326
|
+
return Number.isFinite(t) && t <= nowMs;
|
|
327
|
+
return /complete|finish|final|ended/.test(state);
|
|
328
|
+
};
|
|
304
329
|
recentMatches = extractRows(res.data)
|
|
305
|
-
.
|
|
330
|
+
.filter(isFinished)
|
|
306
331
|
.sort((a, b) => startMs(b) - startMs(a))
|
|
307
332
|
.slice(0, recentLimit)
|
|
308
333
|
.map((row) => normalizeMatch('lol', row, 'completed'));
|
package/package.json
CHANGED