cito-mcp 0.6.0 → 0.6.1

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.
@@ -609,18 +609,18 @@ const GAMES_META = [
609
609
  ];
610
610
  export const listCapabilities = {
611
611
  name: 'list_capabilities',
612
- description: `Curated catalog of cito-mcp tools, games, jobs, and builder recipes.
613
-
614
- When to use:
615
- - Session start or "what can you do?"
616
- - Mapping app screens to tools
617
- - Filtering by game or job (live_board, match_page, team_page, player_form, standings, h2h, schedule, preview, event_card, app_scaffold)
618
-
619
- Prefer over: guessing from memory; exploring raw OpenAPI via call_api.
620
-
621
- Do not use when: you already know the tool and have IDs — call that tool directly.
622
-
623
- Parallel-safe: yes. API calls: 0.
612
+ description: `Curated catalog of cito-mcp tools, games, jobs, and builder recipes.
613
+
614
+ When to use:
615
+ - Session start or "what can you do?"
616
+ - Mapping app screens to tools
617
+ - Filtering by game or job (live_board, match_page, team_page, player_form, standings, h2h, schedule, preview, event_card, app_scaffold)
618
+
619
+ Prefer over: guessing from memory; exploring raw OpenAPI via call_api.
620
+
621
+ Do not use when: you already know the tool and have IDs — call that tool directly.
622
+
623
+ Parallel-safe: yes. API calls: 0.
624
624
  Example: { "game": "cs2", "job": "live_board", "includeExamples": true }`,
625
625
  inputSchema: {
626
626
  type: 'object',
@@ -690,7 +690,7 @@ Example: { "game": "cs2", "job": "live_board", "includeExamples": true }`,
690
690
  game: gameParse.game ?? null,
691
691
  requestId,
692
692
  tookMs: Date.now() - started,
693
- upstreamCalls: 0,
693
+ apiCalls: 0,
694
694
  data: {
695
695
  version: CATALOG_VERSION,
696
696
  tools,
@@ -703,18 +703,18 @@ Example: { "game": "cs2", "job": "live_board", "includeExamples": true }`,
703
703
  };
704
704
  export const apiHealth = {
705
705
  name: 'api_health',
706
- description: `API reachability, API key validity, plan tier, rate-limit headers, and best-effort included games.
707
-
708
- When to use:
709
- - Once per session before heavy work
710
- - After 401/403/UNSUPPORTED_GAME/RATE_LIMIT
711
- - App scaffolding entitlement checks
712
-
713
- Prefer over: probing random game endpoints to test the key.
714
-
715
- Do not use when: you only need live scores — use live_matches.
716
-
717
- Parallel-safe: yes. API calls: 1–6.
706
+ description: `API reachability, API key validity, plan tier, rate-limit headers, and best-effort included games.
707
+
708
+ When to use:
709
+ - Once per session before heavy work
710
+ - After 401/403/UNSUPPORTED_GAME/RATE_LIMIT
711
+ - App scaffolding entitlement checks
712
+
713
+ Prefer over: probing random game endpoints to test the key.
714
+
715
+ Do not use when: you only need live scores — use live_matches.
716
+
717
+ Parallel-safe: yes. API calls: 1–6.
718
718
  Example: { "includeGameProbes": true }`,
719
719
  inputSchema: {
720
720
  type: 'object',
@@ -726,10 +726,10 @@ Example: { "includeGameProbes": true }`,
726
726
  handler: async (args, ctx) => {
727
727
  const started = Date.now();
728
728
  const requestId = newRequestId();
729
- let upstreamCalls = 0;
729
+ let apiCalls = 0;
730
730
  let rateLimit = {};
731
731
  const health = await fetchJson(ctx, '/health');
732
- upstreamCalls += 1;
732
+ apiCalls += 1;
733
733
  rateLimit = health.headers;
734
734
  let keyValid = false;
735
735
  let reachable = health.ok;
@@ -737,7 +737,7 @@ Example: { "includeGameProbes": true }`,
737
737
  let authedStatus = health.status;
738
738
  if (!health.ok) {
739
739
  const fallback = await fetchJson(ctx, '/lol/leagues');
740
- upstreamCalls += 1;
740
+ apiCalls += 1;
741
741
  rateLimit = fallback.headers;
742
742
  if (fallback.ok) {
743
743
  keyValid = true;
@@ -753,14 +753,14 @@ Example: { "includeGameProbes": true }`,
753
753
  source: 'api_health',
754
754
  requestId,
755
755
  tookMs: Date.now() - started,
756
- upstreamCalls,
756
+ apiCalls,
757
757
  rateLimit: fallback.headers,
758
758
  httpStatus: fallback.status,
759
759
  });
760
760
  }
761
761
  else {
762
762
  const cs2 = await fetchJson(ctx, '/cs2');
763
- upstreamCalls += 1;
763
+ apiCalls += 1;
764
764
  rateLimit = cs2.headers;
765
765
  if (cs2.ok) {
766
766
  keyValid = true;
@@ -776,7 +776,7 @@ Example: { "includeGameProbes": true }`,
776
776
  }
777
777
  else {
778
778
  const probe = await fetchJson(ctx, '/lol/leagues');
779
- upstreamCalls += 1;
779
+ apiCalls += 1;
780
780
  rateLimit = { ...rateLimit, ...probe.headers };
781
781
  keyValid = probe.ok || probe.status === 403;
782
782
  if (probe.ok)
@@ -801,7 +801,7 @@ Example: { "includeGameProbes": true }`,
801
801
  const res = await fetchJson(ctx, path);
802
802
  return { game, path, res };
803
803
  }));
804
- upstreamCalls += results.length;
804
+ apiCalls += results.length;
805
805
  for (const { game, res } of results) {
806
806
  probes.push({ game, ok: res.ok, httpStatus: res.status });
807
807
  if (res.ok)
@@ -837,7 +837,7 @@ Example: { "includeGameProbes": true }`,
837
837
  game: null,
838
838
  requestId,
839
839
  tookMs: Date.now() - started,
840
- upstreamCalls,
840
+ apiCalls,
841
841
  rateLimit: mergedRateLimit,
842
842
  data: {
843
843
  reachable,
@@ -888,24 +888,24 @@ function pathAllowed(path) {
888
888
  }
889
889
  export const callApi = {
890
890
  name: 'call_api',
891
- description: `Power escape hatch: allowlisted Cito REST call with unshaped raw JSON in data.raw.
892
-
893
- When to use:
894
- - Long-tail paths not yet curated (Fortnite, CS2 leaderboards, niche stats)
895
- - Debugging payloads while building an app
896
- - User explicitly knows an OpenAPI path
897
-
898
- Prefer curated tools for all standard jobs (live, schedule, profiles, standings, H2H, previews).
899
-
900
- Do not use when: a curated tool covers the outcome. Avoid parallel storms; same plan rate limits apply.
901
-
902
- Paths are relative to the API base, so "/tennis/players", not "/api/v1/tennis/players". Put query params in query or queryJson, not in the path.
903
- Tennis: /tennis/h2h takes players=a,b, or player1_id and player2_id (one form is required). /tennis/players has limit up to 100 (over that is a 422; use /tennis/players/batch for a roster). /tennis/tournaments/calendar needs no year (default this year) and returns every row unless given limit. /tennis/rankings defaults to rank_max=100: pass rank_max=5000 for the whole list. /tennis/schedule returns the whole day unless given limit.
904
-
905
- Path must start with / and match allowlisted prefixes: /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /apex, /tennis, /raf, /bkfc, /bellator, /pride, /strikeforce, /wec, /dream, /elitexc, /affliction, /pfl, /oktagon, /mma.
906
- Rejects absolute URLs and path traversal → PATH_NOT_ALLOWED.
907
-
908
- Parallel-safe: yes but discouraged in bulk. API calls: 1.
891
+ description: `Power escape hatch: allowlisted Cito REST call with unshaped raw JSON in data.raw.
892
+
893
+ When to use:
894
+ - Long-tail paths not yet curated (Fortnite, CS2 leaderboards, niche stats)
895
+ - Debugging payloads while building an app
896
+ - User explicitly knows an OpenAPI path
897
+
898
+ Prefer curated tools for all standard jobs (live, schedule, profiles, standings, H2H, previews).
899
+
900
+ Do not use when: a curated tool covers the outcome. Avoid parallel storms; same plan rate limits apply.
901
+
902
+ Paths are relative to the API base, so "/tennis/players", not "/api/v1/tennis/players". Put query params in query or queryJson, not in the path.
903
+ Tennis: /tennis/h2h takes players=a,b, or player1_id and player2_id (one form is required). /tennis/players has limit up to 100 (over that is a 422; use /tennis/players/batch for a roster). /tennis/tournaments/calendar needs no year (default this year) and returns every row unless given limit. /tennis/rankings defaults to rank_max=100: pass rank_max=5000 for the whole list. /tennis/schedule returns the whole day unless given limit.
904
+
905
+ Path must start with / and match allowlisted prefixes: /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /apex, /tennis, /raf, /bkfc, /bellator, /pride, /strikeforce, /wec, /dream, /elitexc, /affliction, /pfl, /oktagon, /mma.
906
+ Rejects absolute URLs and path traversal → PATH_NOT_ALLOWED.
907
+
908
+ Parallel-safe: yes but discouraged in bulk. API calls: 1.
909
909
  Example: { "method": "GET", "path": "/cs2/rankings/teams", "queryJson": "{\\"page\\":1,\\"limit\\":20}" }`,
910
910
  inputSchema: {
911
911
  type: 'object',
@@ -998,7 +998,7 @@ Example: { "method": "GET", "path": "/cs2/rankings/teams", "queryJson": "{\\"pag
998
998
  source: 'call_api',
999
999
  requestId,
1000
1000
  tookMs: Date.now() - started,
1001
- upstreamCalls: 1,
1001
+ apiCalls: 1,
1002
1002
  rateLimit: res.headers,
1003
1003
  httpStatus: res.status,
1004
1004
  details: { path, method, bodyPreview: present(res.text, 2000) },
@@ -1009,7 +1009,7 @@ Example: { "method": "GET", "path": "/cs2/rankings/teams", "queryJson": "{\\"pag
1009
1009
  game: null,
1010
1010
  requestId,
1011
1011
  tookMs: Date.now() - started,
1012
- upstreamCalls: 1,
1012
+ apiCalls: 1,
1013
1013
  rateLimit: res.headers,
1014
1014
  warnings: ['Unshaped raw response — prefer curated tools when available'],
1015
1015
  data: {
@@ -1031,20 +1031,20 @@ const SPEC_OMITS = {
1031
1031
  };
1032
1032
  export const listRoutes = {
1033
1033
  name: 'list_routes',
1034
- description: `Index of raw REST routes from the live OpenAPI spec: method, path, summary, tag.
1035
-
1036
- When to use:
1037
- - You need a long-tail path for call_api and do not want to guess
1038
- - Checking whether an endpoint exists before building around it
1039
- - Mapping what raw data backs a curated tool
1040
-
1041
- Prefer curated tools for standard jobs — this indexes the escape hatch, it is not a replacement for list_capabilities.
1042
-
1043
- Do not use when: a curated tool already covers the outcome (call list_capabilities instead).
1044
-
1045
- Note: the spec omits /lol entirely, though LoL routes work. Filtering by game=lol returns that caveat rather than an empty list.
1046
-
1047
- Parallel-safe: yes. API calls: 1.
1034
+ description: `Index of raw REST routes from the live OpenAPI spec: method, path, summary, tag.
1035
+
1036
+ When to use:
1037
+ - You need a long-tail path for call_api and do not want to guess
1038
+ - Checking whether an endpoint exists before building around it
1039
+ - Mapping what raw data backs a curated tool
1040
+
1041
+ Prefer curated tools for standard jobs — this indexes the escape hatch, it is not a replacement for list_capabilities.
1042
+
1043
+ Do not use when: a curated tool already covers the outcome (call list_capabilities instead).
1044
+
1045
+ Note: the spec omits /lol entirely, though LoL routes work. Filtering by game=lol returns that caveat rather than an empty list.
1046
+
1047
+ Parallel-safe: yes. API calls: 1.
1048
1048
  Example: { "game": "ufc", "q": "rankings" }`,
1049
1049
  inputSchema: {
1050
1050
  type: 'object',
@@ -1089,7 +1089,7 @@ Example: { "game": "ufc", "q": "rankings" }`,
1089
1089
  source: 'list_routes',
1090
1090
  requestId,
1091
1091
  tookMs: Date.now() - started,
1092
- upstreamCalls: 1,
1092
+ apiCalls: 1,
1093
1093
  rateLimit: res.headers,
1094
1094
  httpStatus: res.status,
1095
1095
  recover: [
@@ -1151,7 +1151,7 @@ Example: { "game": "ufc", "q": "rankings" }`,
1151
1151
  source: 'list_routes',
1152
1152
  requestId,
1153
1153
  tookMs: Date.now() - started,
1154
- upstreamCalls: 1,
1154
+ apiCalls: 1,
1155
1155
  rateLimit: res.headers,
1156
1156
  });
1157
1157
  },
@@ -147,7 +147,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
147
147
  const includeTrends = args.includeTrends !== false;
148
148
  const view = args.view === 'full' ? 'full' : args.view === 'compact' ? 'compact' : 'summary';
149
149
  const partial = [];
150
- let upstreamCalls = 0;
150
+ let apiCalls = 0;
151
151
  let rateLimit = {};
152
152
  let primaryPath = '';
153
153
  if (game === 'lol')
@@ -165,7 +165,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
165
165
  let playerRaw = null;
166
166
  if (game === 'dota2') {
167
167
  const list = await fetchJson(ctx, '/dota2/players', { query: { search: idOrSlug, limit: 5 } });
168
- upstreamCalls += 1;
168
+ apiCalls += 1;
169
169
  rateLimit = list.headers;
170
170
  if (list.ok) {
171
171
  const rows = extractRows(list.data);
@@ -182,7 +182,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
182
182
  }
183
183
  else {
184
184
  const res = await fetchJson(ctx, primaryPath);
185
- upstreamCalls += 1;
185
+ apiCalls += 1;
186
186
  rateLimit = res.headers;
187
187
  if (!res.ok) {
188
188
  return errorEnvelope({
@@ -192,7 +192,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
192
192
  source: 'player_profile',
193
193
  requestId,
194
194
  tookMs: Date.now() - started,
195
- upstreamCalls,
195
+ apiCalls,
196
196
  rateLimit: res.headers,
197
197
  httpStatus: res.status,
198
198
  });
@@ -210,7 +210,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
210
210
  if (game === 'lol') {
211
211
  tasks.push((async () => {
212
212
  const res = await fetchJson(ctx, `/lol/players/${encodeURIComponent(idOrSlug)}/teams`);
213
- upstreamCalls += 1;
213
+ apiCalls += 1;
214
214
  rateLimit = { ...rateLimit, ...res.headers };
215
215
  if (res.ok) {
216
216
  const rows = extractRows(res.data);
@@ -248,7 +248,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
248
248
  const res = await fetchJson(ctx, `/lol/players/${encodeURIComponent(idOrSlug)}/matches`, {
249
249
  query: { limit: String(recentLimit) },
250
250
  });
251
- upstreamCalls += 1;
251
+ apiCalls += 1;
252
252
  rateLimit = { ...rateLimit, ...res.headers };
253
253
  if (res.ok) {
254
254
  const startMs = (row) => {
@@ -269,7 +269,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
269
269
  const names = new Map();
270
270
  await Promise.all(slugs.map(async (sl) => {
271
271
  const t = await fetchJson(ctx, `/lol/teams/${encodeURIComponent(sl)}`);
272
- upstreamCalls += 1;
272
+ apiCalls += 1;
273
273
  if (!t.ok)
274
274
  return;
275
275
  const body = asRecord(t.data) ?? {};
@@ -301,7 +301,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
301
301
  if (includeTrends) {
302
302
  tasks.push((async () => {
303
303
  const res = await fetchJson(ctx, `/tennis/players/${encodeURIComponent(idOrSlug)}/stats`);
304
- upstreamCalls += 1;
304
+ apiCalls += 1;
305
305
  rateLimit = { ...rateLimit, ...res.headers };
306
306
  if (res.ok)
307
307
  career = unwrapPayload(res.data);
@@ -318,7 +318,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
318
318
  const res = await fetchJson(ctx, `/tennis/players/${encodeURIComponent(idOrSlug)}/matches`, {
319
319
  query: { page_size: recentLimit, ...(view === 'compact' ? { view: 'summary' } : {}) },
320
320
  });
321
- upstreamCalls += 1;
321
+ apiCalls += 1;
322
322
  rateLimit = { ...rateLimit, ...res.headers };
323
323
  if (res.ok) {
324
324
  const envelope = asRecord(res.data) ?? {};
@@ -342,7 +342,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
342
342
  ]) {
343
343
  tasks.push((async () => {
344
344
  const res = await fetchJson(ctx, path);
345
- upstreamCalls += 1;
345
+ apiCalls += 1;
346
346
  rateLimit = { ...rateLimit, ...res.headers };
347
347
  if (!res.ok) {
348
348
  partial.push(partialFromRejection(section, {
@@ -377,7 +377,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
377
377
  const res = await fetchJson(ctx, `/dota2/players/${encodeURIComponent(idOrSlug)}/radar`, {
378
378
  query,
379
379
  });
380
- upstreamCalls += 1;
380
+ apiCalls += 1;
381
381
  rateLimit = { ...rateLimit, ...res.headers };
382
382
  if (res.ok) {
383
383
  radar = res.data;
@@ -400,7 +400,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
400
400
  if (game === 'cod') {
401
401
  tasks.push((async () => {
402
402
  const res = await fetchJson(ctx, `/cod/players/${encodeURIComponent(idOrSlug)}/stats`);
403
- upstreamCalls += 1;
403
+ apiCalls += 1;
404
404
  rateLimit = { ...rateLimit, ...res.headers };
405
405
  if (res.ok) {
406
406
  seasonStats = res.data;
@@ -418,7 +418,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
418
418
  const res = await fetchJson(ctx, `/cod/players/${encodeURIComponent(idOrSlug)}/matches`, {
419
419
  query: { limit: recentLimit },
420
420
  });
421
- upstreamCalls += 1;
421
+ apiCalls += 1;
422
422
  rateLimit = { ...rateLimit, ...res.headers };
423
423
  if (res.ok) {
424
424
  recentMatches = extractRows(res.data)
@@ -438,7 +438,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
438
438
  const fighterKey = slug || idOrSlug;
439
439
  tasks.push((async () => {
440
440
  const res = await fetchJson(ctx, `/ufc/fighters/${encodeURIComponent(fighterKey)}/stats`);
441
- upstreamCalls += 1;
441
+ apiCalls += 1;
442
442
  rateLimit = { ...rateLimit, ...res.headers };
443
443
  if (res.ok) {
444
444
  const metrics = unwrapPayload(res.data);
@@ -456,7 +456,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
456
456
  const res = await fetchJson(ctx, `/ufc/fighters/${encodeURIComponent(fighterKey)}/fights`, {
457
457
  query: { limit: recentLimit },
458
458
  });
459
- upstreamCalls += 1;
459
+ apiCalls += 1;
460
460
  rateLimit = { ...rateLimit, ...res.headers };
461
461
  if (res.ok) {
462
462
  recentMatches = extractRows(res.data)
@@ -477,7 +477,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
477
477
  const hist = await fetchJson(ctx, `/ufc/fighters/${encodeURIComponent(fighterKey)}/history`, {
478
478
  query: { limit: recentLimit },
479
479
  });
480
- upstreamCalls += 1;
480
+ apiCalls += 1;
481
481
  rateLimit = { ...rateLimit, ...hist.headers };
482
482
  if (hist.ok) {
483
483
  recentMatches = extractRows(hist.data)
@@ -507,7 +507,7 @@ Example: { "game": "cs2", "playerId": "cs2-player-1", "recentLimit": 10, "includ
507
507
  game,
508
508
  requestId,
509
509
  tookMs: Date.now() - started,
510
- upstreamCalls,
510
+ apiCalls,
511
511
  rateLimit,
512
512
  partial: partial.length ? partial : undefined,
513
513
  entities: {
@@ -656,7 +656,7 @@ Parallel-safe: yes. API calls: 1.`,
656
656
  source: 'player_form',
657
657
  requestId,
658
658
  tookMs: Date.now() - started,
659
- upstreamCalls: 1,
659
+ apiCalls: 1,
660
660
  httpStatus: res.status,
661
661
  rateLimit: res.headers,
662
662
  });
@@ -695,7 +695,7 @@ Parallel-safe: yes. API calls: 1.`,
695
695
  game,
696
696
  requestId,
697
697
  tookMs: Date.now() - started,
698
- upstreamCalls: 1,
698
+ apiCalls: 1,
699
699
  rateLimit: res.headers,
700
700
  entities: {
701
701
  games: [game],
@@ -805,7 +805,7 @@ Parallel-safe: yes. API calls: 1.`,
805
805
  source: 'player_matches',
806
806
  requestId,
807
807
  tookMs: Date.now() - started,
808
- upstreamCalls: 1,
808
+ apiCalls: 1,
809
809
  httpStatus: res.status,
810
810
  rateLimit: res.headers,
811
811
  recover: ['Check the player id with search_entities'],
@@ -873,7 +873,7 @@ Parallel-safe: yes. API calls: 1.`,
873
873
  game,
874
874
  requestId,
875
875
  tookMs: Date.now() - started,
876
- upstreamCalls: 1,
876
+ apiCalls: 1,
877
877
  rateLimit: res.headers,
878
878
  entities: { games: [game], ids: { playerId } },
879
879
  data: {
@@ -982,7 +982,7 @@ API calls: 1.`,
982
982
  source: 'player_stats',
983
983
  requestId,
984
984
  tookMs: Date.now() - started,
985
- upstreamCalls: 1,
985
+ apiCalls: 1,
986
986
  httpStatus: res.status,
987
987
  rateLimit: res.headers,
988
988
  recover: ['Check the player id with search_entities'],
@@ -1071,7 +1071,7 @@ API calls: 1.`,
1071
1071
  game,
1072
1072
  requestId,
1073
1073
  tookMs: Date.now() - started,
1074
- upstreamCalls: 1,
1074
+ apiCalls: 1,
1075
1075
  rateLimit: res.headers,
1076
1076
  entities: { games: [game], ids: { playerId } },
1077
1077
  data: {
@@ -193,7 +193,7 @@ Parallel-safe: yes. API calls: 1.`,
193
193
  source: 'rankings_movers',
194
194
  requestId,
195
195
  tookMs: Date.now() - started,
196
- upstreamCalls: 1,
196
+ apiCalls: 1,
197
197
  httpStatus: res.status,
198
198
  rateLimit: res.headers,
199
199
  });
@@ -248,7 +248,7 @@ Parallel-safe: yes. API calls: 1.`,
248
248
  game,
249
249
  requestId,
250
250
  tookMs: Date.now() - started,
251
- upstreamCalls: 1,
251
+ apiCalls: 1,
252
252
  rateLimit: res.headers,
253
253
  warnings: warnings.length ? warnings : undefined,
254
254
  data: {
@@ -370,7 +370,7 @@ Parallel-safe: yes. API calls: 1.`,
370
370
  source: 'player_rankings_history',
371
371
  requestId,
372
372
  tookMs: Date.now() - started,
373
- upstreamCalls: 1,
373
+ apiCalls: 1,
374
374
  httpStatus: res.status,
375
375
  rateLimit: res.headers,
376
376
  recover: ['Check the player id with search_entities'],
@@ -390,10 +390,10 @@ Parallel-safe: yes. API calls: 1.`,
390
390
  const inWindow = sinceRaw
391
391
  ? allHistory.filter((h) => h.date !== null && h.date >= sinceRaw)
392
392
  : allHistory;
393
- const pagedUpstream = typeof body.total === 'number' && typeof body.order === 'string';
393
+ const pagedApi = typeof body.total === 'number' && typeof body.order === 'string';
394
394
  let total;
395
395
  let history;
396
- if (pagedUpstream) {
396
+ if (pagedApi) {
397
397
  total = body.total;
398
398
  history = body.order === 'desc' ? [...inWindow].reverse() : inWindow;
399
399
  }
@@ -404,7 +404,7 @@ Parallel-safe: yes. API calls: 1.`,
404
404
  }
405
405
  const omittedNewer = Math.min(total, (page - 1) * limit);
406
406
  const omittedOlder = Math.max(0, total - omittedNewer - history.length);
407
- const hasMore = pagedUpstream && typeof body.has_next === 'boolean' ? body.has_next : omittedOlder > 0;
407
+ const hasMore = pagedApi && typeof body.has_next === 'boolean' ? body.has_next : omittedOlder > 0;
408
408
  const best = history.reduce((acc, h) => {
409
409
  if (h.rank === null)
410
410
  return acc;
@@ -425,7 +425,7 @@ Parallel-safe: yes. API calls: 1.`,
425
425
  game,
426
426
  requestId,
427
427
  tookMs: Date.now() - started,
428
- upstreamCalls: 1,
428
+ apiCalls: 1,
429
429
  rateLimit: res.headers,
430
430
  data: {
431
431
  title: `${pickString(body.player_name) ?? playerId} ranking history`,