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.
- package/dist/client.js +8 -8
- package/dist/envelope.js +4 -4
- package/dist/install.js +15 -15
- package/dist/pacing.js +2 -2
- package/dist/scrub.js +58 -20
- package/dist/tools/combat.js +96 -96
- package/dist/tools/insight.js +36 -36
- package/dist/tools/leaderboard.js +4 -4
- package/dist/tools/live.js +41 -41
- package/dist/tools/match.js +25 -21
- package/dist/tools/meta.js +69 -69
- package/dist/tools/player.js +23 -23
- package/dist/tools/rankings.js +7 -7
- package/dist/tools/resolve.js +53 -53
- package/dist/tools/schedule.js +8 -8
- package/dist/tools/standings.js +23 -23
- package/dist/tools/team.js +88 -88
- package/dist/tools/tournaments.js +2 -2
- package/dist/tools/webhooks.js +2 -2
- package/package.json +51 -52
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MAX_RATE_LIMIT_RETRIES,
|
|
1
|
+
import { MAX_RATE_LIMIT_RETRIES, MAX_SERVER_RETRIES, MAX_WAIT_MS, SERVER_BACKOFF_MS, classify429, getClock, jitterMs, newCallBudget, pacerFor, } from './pacing.js';
|
|
2
2
|
import { PACKAGE_VERSION } from './version.js';
|
|
3
3
|
export const DEFAULT_API_BASE = 'https://api.citoapi.com/api/v1';
|
|
4
4
|
export const DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024;
|
|
@@ -87,7 +87,7 @@ export async function fetchJson(ctx, path, opts) {
|
|
|
87
87
|
const pacer = pacerFor(ctx.apiKey);
|
|
88
88
|
const clock = getClock();
|
|
89
89
|
let rateLimitRetries = 0;
|
|
90
|
-
let
|
|
90
|
+
let serverRetries = 0;
|
|
91
91
|
for (;;) {
|
|
92
92
|
const gate = await pacer.acquire(budget);
|
|
93
93
|
if (!gate.ok) {
|
|
@@ -117,7 +117,7 @@ export async function fetchJson(ctx, path, opts) {
|
|
|
117
117
|
text,
|
|
118
118
|
url,
|
|
119
119
|
rateLimit: event,
|
|
120
|
-
retries: rateLimitRetries +
|
|
120
|
+
retries: rateLimitRetries + serverRetries,
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
let response;
|
|
@@ -162,9 +162,9 @@ export async function fetchJson(ctx, path, opts) {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
else if (response.status === 502 || response.status === 503) {
|
|
165
|
-
const wait =
|
|
166
|
-
if (
|
|
167
|
-
|
|
165
|
+
const wait = SERVER_BACKOFF_MS + jitterMs();
|
|
166
|
+
if (serverRetries < MAX_SERVER_RETRIES && clock.now() + wait < budget.deadline) {
|
|
167
|
+
serverRetries += 1;
|
|
168
168
|
budget.retries += 1;
|
|
169
169
|
budget.waitedMs += wait;
|
|
170
170
|
log(`${response.status} on ${path}; retry after ${wait} ms`);
|
|
@@ -184,8 +184,8 @@ export async function fetchJson(ctx, path, opts) {
|
|
|
184
184
|
};
|
|
185
185
|
if (limited)
|
|
186
186
|
result.rateLimit = limited;
|
|
187
|
-
if (rateLimitRetries +
|
|
188
|
-
result.retries = rateLimitRetries +
|
|
187
|
+
if (rateLimitRetries + serverRetries > 0)
|
|
188
|
+
result.retries = rateLimitRetries + serverRetries;
|
|
189
189
|
return result;
|
|
190
190
|
}
|
|
191
191
|
}
|
package/dist/envelope.js
CHANGED
|
@@ -80,8 +80,8 @@ export function successEnvelope(args) {
|
|
|
80
80
|
meta.requestId = args.requestId;
|
|
81
81
|
if (args.tookMs !== undefined)
|
|
82
82
|
meta.tookMs = args.tookMs;
|
|
83
|
-
if (args.
|
|
84
|
-
meta.apiCalls = args.
|
|
83
|
+
if (args.apiCalls !== undefined)
|
|
84
|
+
meta.apiCalls = args.apiCalls;
|
|
85
85
|
if (args.rateLimit)
|
|
86
86
|
meta.rateLimit = args.rateLimit;
|
|
87
87
|
if (args.warnings?.length)
|
|
@@ -113,8 +113,8 @@ export function errorEnvelope(args) {
|
|
|
113
113
|
meta.requestId = args.requestId;
|
|
114
114
|
if (args.tookMs !== undefined)
|
|
115
115
|
meta.tookMs = args.tookMs;
|
|
116
|
-
if (args.
|
|
117
|
-
meta.apiCalls = args.
|
|
116
|
+
if (args.apiCalls !== undefined)
|
|
117
|
+
meta.apiCalls = args.apiCalls;
|
|
118
118
|
if (args.rateLimit)
|
|
119
119
|
meta.rateLimit = args.rateLimit;
|
|
120
120
|
const error = {
|
package/dist/install.js
CHANGED
|
@@ -222,21 +222,21 @@ function parseArgs(argv) {
|
|
|
222
222
|
}
|
|
223
223
|
return out;
|
|
224
224
|
}
|
|
225
|
-
const HELP = `cito-mcp install — configure the Cito MCP server for your editors
|
|
226
|
-
|
|
227
|
-
Usage:
|
|
228
|
-
npx cito-mcp install --key cito_xxx
|
|
229
|
-
npx cito-mcp install --key cito_xxx --client cursor,claude-code
|
|
230
|
-
npx cito-mcp install --dry-run
|
|
231
|
-
|
|
232
|
-
Options:
|
|
233
|
-
--key <key> Cito API key. Falls back to $CITO_API_KEY.
|
|
234
|
-
--client <ids> Comma-separated: claude-code, claude-desktop, cursor, windsurf, codex, grok.
|
|
235
|
-
Default: every client detected on this machine.
|
|
236
|
-
--base <url> Override API base (staging / self-hosted).
|
|
237
|
-
--dry-run, -n Show what would change; write nothing.
|
|
238
|
-
--help, -h This message.
|
|
239
|
-
|
|
225
|
+
const HELP = `cito-mcp install — configure the Cito MCP server for your editors
|
|
226
|
+
|
|
227
|
+
Usage:
|
|
228
|
+
npx cito-mcp install --key cito_xxx
|
|
229
|
+
npx cito-mcp install --key cito_xxx --client cursor,claude-code
|
|
230
|
+
npx cito-mcp install --dry-run
|
|
231
|
+
|
|
232
|
+
Options:
|
|
233
|
+
--key <key> Cito API key. Falls back to $CITO_API_KEY.
|
|
234
|
+
--client <ids> Comma-separated: claude-code, claude-desktop, cursor, windsurf, codex, grok.
|
|
235
|
+
Default: every client detected on this machine.
|
|
236
|
+
--base <url> Override API base (staging / self-hosted).
|
|
237
|
+
--dry-run, -n Show what would change; write nothing.
|
|
238
|
+
--help, -h This message.
|
|
239
|
+
|
|
240
240
|
Get a key at https://citoapi.com/dashboard`;
|
|
241
241
|
export async function runInstall(argv) {
|
|
242
242
|
const args = parseArgs(argv);
|
package/dist/pacing.js
CHANGED
|
@@ -3,8 +3,8 @@ export const CONSTRAINED_CONCURRENT_PER_KEY = 2;
|
|
|
3
3
|
export const CONSTRAINED_WINDOW_MS = 60_000;
|
|
4
4
|
export const MAX_WAIT_MS = 20_000;
|
|
5
5
|
export const MAX_RATE_LIMIT_RETRIES = 2;
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
6
|
+
export const MAX_SERVER_RETRIES = 1;
|
|
7
|
+
export const SERVER_BACKOFF_MS = 750;
|
|
8
8
|
export const CALL_BUDGET_MS = 45_000;
|
|
9
9
|
const DEFAULT_RETRY_AFTER_MS = 2_000;
|
|
10
10
|
const WINDOW_MS = 60_000;
|
package/dist/scrub.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
const INTERNAL_KEYS = new Set([
|
|
2
3
|
'emptyCardHeal',
|
|
3
4
|
'llmValidation',
|
|
4
5
|
'workerHostHint',
|
|
5
|
-
'scrapeMeta',
|
|
6
6
|
'fetchMeta',
|
|
7
7
|
'cacheKey',
|
|
8
8
|
'failureSteps',
|
|
@@ -12,8 +12,6 @@ const INTERNAL_KEYS = new Set([
|
|
|
12
12
|
'sourceUrl',
|
|
13
13
|
'sourceUrls',
|
|
14
14
|
'sourceIds',
|
|
15
|
-
'wikiUrl',
|
|
16
|
-
'wiki_url',
|
|
17
15
|
'jsonLd',
|
|
18
16
|
'_mergeSource',
|
|
19
17
|
'_provenance',
|
|
@@ -32,21 +30,17 @@ const INTERNAL_KEYS = new Set([
|
|
|
32
30
|
'profileImageAttemptedAt',
|
|
33
31
|
'profileImageAttempts',
|
|
34
32
|
'profileImageError',
|
|
35
|
-
'profileImageSteamAttemptedAt',
|
|
36
33
|
'profileImageWorker',
|
|
37
34
|
'ratingSource',
|
|
38
35
|
'requiredSourceData',
|
|
39
36
|
'rawText',
|
|
40
37
|
'raw_text',
|
|
41
|
-
'scrapedAt',
|
|
42
|
-
'scraped_at',
|
|
43
38
|
'sourceSummary',
|
|
44
39
|
'sourceNote',
|
|
45
40
|
'durableStatsTables',
|
|
46
41
|
'lineupSource',
|
|
47
42
|
'mapMediaSource',
|
|
48
43
|
'rosterSource',
|
|
49
|
-
'upstream',
|
|
50
44
|
'liveCandidateWindowMinutes',
|
|
51
45
|
'rowHash',
|
|
52
46
|
'row_hash',
|
|
@@ -73,20 +67,64 @@ const INTERNAL_KEYS = new Set([
|
|
|
73
67
|
'data_source',
|
|
74
68
|
'date_source',
|
|
75
69
|
'meta_source',
|
|
76
|
-
'esportsMatchId',
|
|
77
|
-
'esportsGameId',
|
|
78
|
-
'esports_match_id',
|
|
79
|
-
'riotId',
|
|
80
|
-
'riot_id',
|
|
81
|
-
'steamId',
|
|
82
|
-
'steam_id',
|
|
83
|
-
'oddsKey',
|
|
84
|
-
'odds_key',
|
|
85
|
-
'provider',
|
|
86
|
-
'providers',
|
|
87
70
|
]);
|
|
88
71
|
const ORIGIN_WORD_KEY = /(^|_)sources?($|_)|[a-z]Sources?($|[A-Z])|^_?sources?[A-Z]/;
|
|
89
|
-
const
|
|
72
|
+
const DENIED_WORDS = new Set([
|
|
73
|
+
'007d859ffc608f3a',
|
|
74
|
+
'017055b00735cdaa',
|
|
75
|
+
'0991545f91164c02',
|
|
76
|
+
'0a86e4e8778e5bc4',
|
|
77
|
+
'211022d4dba1354e',
|
|
78
|
+
'2273bc83a7916501',
|
|
79
|
+
'343406fa95fefe9a',
|
|
80
|
+
'3511b94bd7795e3b',
|
|
81
|
+
'3fbfbeeeaddc4fe5',
|
|
82
|
+
'417ccb9b693bc11d',
|
|
83
|
+
'438338424376242e',
|
|
84
|
+
'53a828d1313e2f24',
|
|
85
|
+
'56279b500aeff149',
|
|
86
|
+
'63919bdfdccc4e03',
|
|
87
|
+
'68ec9e984bd969e2',
|
|
88
|
+
'735bda6297e559c5',
|
|
89
|
+
'7a9040b69cc466a0',
|
|
90
|
+
'7e25e5e9baf788fe',
|
|
91
|
+
'827ce7e74edb79b2',
|
|
92
|
+
'82d2e789811fae32',
|
|
93
|
+
'93b0c3823ffe6d0b',
|
|
94
|
+
'a85f3c568720d7da',
|
|
95
|
+
'aab85fb91587ac06',
|
|
96
|
+
'aaf4b5714130cfbd',
|
|
97
|
+
'ac4399e79599c5f9',
|
|
98
|
+
'b71515611a06c04e',
|
|
99
|
+
'b769513d718b1bdd',
|
|
100
|
+
'b8e91b77223e5c17',
|
|
101
|
+
'c833542aacad261c',
|
|
102
|
+
'cca8e34d07dda457',
|
|
103
|
+
'd656bca8c6e48174',
|
|
104
|
+
'dc2bcd7bbfe8773e',
|
|
105
|
+
'e08ad232764c86ab',
|
|
106
|
+
'e3bd135d984a0b44',
|
|
107
|
+
'e924b9eb04be6af2',
|
|
108
|
+
'f2b71a5a46b415e3',
|
|
109
|
+
'f578f53f4c52c9c9',
|
|
110
|
+
'fb25211c8b74e564',
|
|
111
|
+
]);
|
|
112
|
+
const DENIED_MEMO = new Map();
|
|
113
|
+
function wordHash(word) {
|
|
114
|
+
return createHash('sha256').update(`cito-mcp-key-v1:${word}`).digest('hex').slice(0, 16);
|
|
115
|
+
}
|
|
116
|
+
function hasDeniedWord(key) {
|
|
117
|
+
let hit = DENIED_MEMO.get(key);
|
|
118
|
+
if (hit === undefined) {
|
|
119
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').split(/[\s_-]+/).filter(Boolean).map((w) => w.toLowerCase());
|
|
120
|
+
const tries = [...words, ...words.slice(1).map((w, i) => `${words[i]}${w}`)];
|
|
121
|
+
hit = tries.some((w) => DENIED_WORDS.has(wordHash(w)));
|
|
122
|
+
if (DENIED_MEMO.size > 20_000)
|
|
123
|
+
DENIED_MEMO.clear();
|
|
124
|
+
DENIED_MEMO.set(key, hit);
|
|
125
|
+
}
|
|
126
|
+
return hit;
|
|
127
|
+
}
|
|
90
128
|
const PORTRAIT_LICENCE_KEY = /^(portrait|image|photo|picture)_?source_?url$/i;
|
|
91
129
|
const STATUS_PARENTS = new Set(['dataAvailability', 'health']);
|
|
92
130
|
const STATUS_KEYS = new Set(['strategy', 'samples']);
|
|
@@ -104,7 +142,7 @@ function isInternal(key, parentKey) {
|
|
|
104
142
|
return false;
|
|
105
143
|
return (INTERNAL_KEYS.has(key) ||
|
|
106
144
|
ORIGIN_WORD_KEY.test(key) ||
|
|
107
|
-
|
|
145
|
+
hasDeniedWord(key) ||
|
|
108
146
|
(STATUS_KEYS.has(key) && STATUS_PARENTS.has(parentKey)));
|
|
109
147
|
}
|
|
110
148
|
function scrub(node, parentKey, inError) {
|
package/dist/tools/combat.js
CHANGED
|
@@ -82,7 +82,7 @@ function payload(res) {
|
|
|
82
82
|
const body = asRecord(res.data);
|
|
83
83
|
return body && 'data' in body ? body.data : res.data;
|
|
84
84
|
}
|
|
85
|
-
function
|
|
85
|
+
function responseNotes(res) {
|
|
86
86
|
const meta = asRecord(asRecord(res.data)?.meta);
|
|
87
87
|
const out = {};
|
|
88
88
|
if (typeof meta?.note === 'string')
|
|
@@ -114,7 +114,7 @@ function archiveGap(d, league, message, recover, calls = 0) {
|
|
|
114
114
|
source: d.source,
|
|
115
115
|
requestId: d.requestId,
|
|
116
116
|
tookMs: Date.now() - d.started,
|
|
117
|
-
|
|
117
|
+
apiCalls: calls,
|
|
118
118
|
retryable: false,
|
|
119
119
|
recover,
|
|
120
120
|
});
|
|
@@ -178,21 +178,21 @@ function begin(source) {
|
|
|
178
178
|
}
|
|
179
179
|
export const combatEvents = {
|
|
180
180
|
name: 'combat_events',
|
|
181
|
-
description: `RAF wrestling, BKFC bare-knuckle or MMA archive (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction) events.
|
|
182
|
-
|
|
183
|
-
When to use:
|
|
184
|
-
- "When is the next RAF / BKFC card?", "recent BKFC results", "PRIDE results", "Bellator cards from 2015", schedule pages
|
|
185
|
-
- Getting an event slug for combat_card
|
|
186
|
-
|
|
187
|
-
Each event carries slug, name, date, local start time + timezone, venue/location, status and bout counts.
|
|
188
|
-
The MMA archives (bellator, pride, strikeforce, wec, dream, elitexc, affliction, and mma = all of them, each
|
|
189
|
-
event tagged with its real promotion) are closed: "when" defaults to "recent" (newest card first), and "upcoming"
|
|
190
|
-
returns an empty list with data.note instead of failing.
|
|
191
|
-
|
|
192
|
-
Prefer over: call_api /{league}/events.
|
|
193
|
-
Do not use when: you already have the event slug and want its bouts (combat_card).
|
|
194
|
-
|
|
195
|
-
Parallel-safe: yes. API calls: 1.
|
|
181
|
+
description: `RAF wrestling, BKFC bare-knuckle or MMA archive (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction) events.
|
|
182
|
+
|
|
183
|
+
When to use:
|
|
184
|
+
- "When is the next RAF / BKFC card?", "recent BKFC results", "PRIDE results", "Bellator cards from 2015", schedule pages
|
|
185
|
+
- Getting an event slug for combat_card
|
|
186
|
+
|
|
187
|
+
Each event carries slug, name, date, local start time + timezone, venue/location, status and bout counts.
|
|
188
|
+
The MMA archives (bellator, pride, strikeforce, wec, dream, elitexc, affliction, and mma = all of them, each
|
|
189
|
+
event tagged with its real promotion) are closed: "when" defaults to "recent" (newest card first), and "upcoming"
|
|
190
|
+
returns an empty list with data.note instead of failing.
|
|
191
|
+
|
|
192
|
+
Prefer over: call_api /{league}/events.
|
|
193
|
+
Do not use when: you already have the event slug and want its bouts (combat_card).
|
|
194
|
+
|
|
195
|
+
Parallel-safe: yes. API calls: 1.
|
|
196
196
|
Example: { "league": "bkfc", "when": "upcoming", "limit": 5 }`,
|
|
197
197
|
inputSchema: {
|
|
198
198
|
type: 'object',
|
|
@@ -228,7 +228,7 @@ Example: { "league": "bkfc", "when": "upcoming", "limit": 5 }`,
|
|
|
228
228
|
source: d.source,
|
|
229
229
|
requestId: d.requestId,
|
|
230
230
|
tookMs: Date.now() - d.started,
|
|
231
|
-
|
|
231
|
+
apiCalls: 0,
|
|
232
232
|
});
|
|
233
233
|
}
|
|
234
234
|
const res = await fetchJson(ctx, `/${league}/events/${when}`, { query: { limit } });
|
|
@@ -236,35 +236,35 @@ Example: { "league": "bkfc", "when": "upcoming", "limit": 5 }`,
|
|
|
236
236
|
return httpError(d, league, res, `${league} ${when} events`);
|
|
237
237
|
const events = extractRows(res.data).slice(0, limit);
|
|
238
238
|
return successEnvelope({
|
|
239
|
-
data: { league, leagueName: LEAGUE_NAMES[league], when, count: events.length, events, ...
|
|
239
|
+
data: { league, leagueName: LEAGUE_NAMES[league], when, count: events.length, events, ...responseNotes(res) },
|
|
240
240
|
game: league,
|
|
241
241
|
source: d.source,
|
|
242
242
|
requestId: d.requestId,
|
|
243
243
|
tookMs: Date.now() - d.started,
|
|
244
|
-
|
|
244
|
+
apiCalls: 1,
|
|
245
245
|
rateLimit: res.headers,
|
|
246
246
|
});
|
|
247
247
|
},
|
|
248
248
|
};
|
|
249
249
|
export const combatCard = {
|
|
250
250
|
name: 'combat_card',
|
|
251
|
-
description: `COMPOSITE fight card for one RAF, BKFC or MMA archive event: event identity + bouts in running order with results.
|
|
252
|
-
|
|
253
|
-
When to use:
|
|
254
|
-
- "RAF 14 card", "BKFC 95 results", "Bellator 142 card", "PRIDE 34", "who won the main event at the last BKFC?"
|
|
255
|
-
- Event pages and results pages
|
|
256
|
-
|
|
257
|
-
event accepts a slug (e.g. "raf13", "bkfc-95-newark", "bellator-142", "pride-34", "wec-48") or "next" / "last".
|
|
258
|
-
Bouts come main event first; each carries weight class, title stakes, both sides (athletes for RAF, red/blue fighters for BKFC with
|
|
259
|
-
punch totals, red/blue fighters for the MMA archives with UFC-format totals and ufcFighterSlug), and once finished
|
|
260
|
-
the winner and result (RAF: score, takedowns, method; BKFC/MMA archives: method, round, time).
|
|
261
|
-
MMA archives are closed: event defaults to "last" (the most recent card; for mma, across every promotion), and
|
|
262
|
-
"next" is a non-retryable NOT_IMPLEMENTED (there are no upcoming cards).
|
|
263
|
-
|
|
264
|
-
Prefer over: combat_events + call_api per bout.
|
|
265
|
-
Do not use when: you need one bout's period/round breakdown (combat_bout).
|
|
266
|
-
|
|
267
|
-
Parallel-safe: yes. API calls: 2–3.
|
|
251
|
+
description: `COMPOSITE fight card for one RAF, BKFC or MMA archive event: event identity + bouts in running order with results.
|
|
252
|
+
|
|
253
|
+
When to use:
|
|
254
|
+
- "RAF 14 card", "BKFC 95 results", "Bellator 142 card", "PRIDE 34", "who won the main event at the last BKFC?"
|
|
255
|
+
- Event pages and results pages
|
|
256
|
+
|
|
257
|
+
event accepts a slug (e.g. "raf13", "bkfc-95-newark", "bellator-142", "pride-34", "wec-48") or "next" / "last".
|
|
258
|
+
Bouts come main event first; each carries weight class, title stakes, both sides (athletes for RAF, red/blue fighters for BKFC with
|
|
259
|
+
punch totals, red/blue fighters for the MMA archives with UFC-format totals and ufcFighterSlug), and once finished
|
|
260
|
+
the winner and result (RAF: score, takedowns, method; BKFC/MMA archives: method, round, time).
|
|
261
|
+
MMA archives are closed: event defaults to "last" (the most recent card; for mma, across every promotion), and
|
|
262
|
+
"next" is a non-retryable NOT_IMPLEMENTED (there are no upcoming cards).
|
|
263
|
+
|
|
264
|
+
Prefer over: combat_events + call_api per bout.
|
|
265
|
+
Do not use when: you need one bout's period/round breakdown (combat_bout).
|
|
266
|
+
|
|
267
|
+
Parallel-safe: yes. API calls: 2–3.
|
|
268
268
|
Example: { "league": "raf", "event": "last" }`,
|
|
269
269
|
inputSchema: {
|
|
270
270
|
type: 'object',
|
|
@@ -298,7 +298,7 @@ Example: { "league": "raf", "event": "last" }`,
|
|
|
298
298
|
return httpError(d, league, list, `${league} ${which} events`);
|
|
299
299
|
const first = asRecord(extractRows(list.data)[0]);
|
|
300
300
|
if (!first?.slug) {
|
|
301
|
-
const windowed = !!
|
|
301
|
+
const windowed = !!responseNotes(list).historyWindow;
|
|
302
302
|
return errorEnvelope({
|
|
303
303
|
code: 'NOT_FOUND',
|
|
304
304
|
message: windowed
|
|
@@ -308,8 +308,8 @@ Example: { "league": "raf", "event": "last" }`,
|
|
|
308
308
|
source: d.source,
|
|
309
309
|
requestId: d.requestId,
|
|
310
310
|
tookMs: Date.now() - d.started,
|
|
311
|
-
|
|
312
|
-
...(windowed ? { retryable: false, details: { historyWindow:
|
|
311
|
+
apiCalls: calls,
|
|
312
|
+
...(windowed ? { retryable: false, details: { historyWindow: responseNotes(list).historyWindow } } : {}),
|
|
313
313
|
});
|
|
314
314
|
}
|
|
315
315
|
slug = String(first.slug);
|
|
@@ -338,7 +338,7 @@ Example: { "league": "raf", "event": "last" }`,
|
|
|
338
338
|
source: d.source,
|
|
339
339
|
requestId: d.requestId,
|
|
340
340
|
tookMs: Date.now() - d.started,
|
|
341
|
-
|
|
341
|
+
apiCalls: calls,
|
|
342
342
|
rateLimit: bouts.headers ?? ev.headers,
|
|
343
343
|
partial,
|
|
344
344
|
truncated: rows.length > limit
|
|
@@ -349,22 +349,22 @@ Example: { "league": "raf", "event": "last" }`,
|
|
|
349
349
|
};
|
|
350
350
|
export const combatBout = {
|
|
351
351
|
name: 'combat_bout',
|
|
352
|
-
description: `One RAF, BKFC or MMA archive bout in full, with its scoring breakdown.
|
|
353
|
-
|
|
354
|
-
RAF: winner, method (decision / tech fall / pin / injury), final score, takedowns, and per-PERIOD scores.
|
|
355
|
-
BKFC: winner, method, round, time, and per-ROUND punch stats for red and blue (landed/thrown, head/body,
|
|
356
|
-
distance/clinch, knockdowns). BKFC round stats need the Starter plan; on Free they come back as a partial note.
|
|
357
|
-
MMA archives (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction, mma): winner, method, round, time,
|
|
358
|
-
finish (position / submission), referee, judges' scores, each corner's whole-fight totals, and per-ROUND official
|
|
359
|
-
stats in UFC's format (significantStrikes "13 of 18", totalStrikes, takedowns, head/body/leg, distance/clinch/ground,
|
|
360
|
-
knockdowns, submissionAttempts, reversals, controlTime "1:42"). PRIDE's first round is 10 minutes. The archives are
|
|
361
|
-
older than the Free/Starter history window: on those plans rounds come back as a HISTORY_WINDOW_EXCEEDED partial
|
|
362
|
-
note while the result still returns.
|
|
363
|
-
|
|
364
|
-
Get boutId from combat_card or combat_athlete (e.g. "raf13--belal-muhammad--colby-covington", "bellator-5600").
|
|
365
|
-
MMA archive bout ids are "<promotion>-<fightId>"; a bare fight id resolves in any MMA archive league.
|
|
366
|
-
|
|
367
|
-
Parallel-safe: yes. API calls: 1–2.
|
|
352
|
+
description: `One RAF, BKFC or MMA archive bout in full, with its scoring breakdown.
|
|
353
|
+
|
|
354
|
+
RAF: winner, method (decision / tech fall / pin / injury), final score, takedowns, and per-PERIOD scores.
|
|
355
|
+
BKFC: winner, method, round, time, and per-ROUND punch stats for red and blue (landed/thrown, head/body,
|
|
356
|
+
distance/clinch, knockdowns). BKFC round stats need the Starter plan; on Free they come back as a partial note.
|
|
357
|
+
MMA archives (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction, mma): winner, method, round, time,
|
|
358
|
+
finish (position / submission), referee, judges' scores, each corner's whole-fight totals, and per-ROUND official
|
|
359
|
+
stats in UFC's format (significantStrikes "13 of 18", totalStrikes, takedowns, head/body/leg, distance/clinch/ground,
|
|
360
|
+
knockdowns, submissionAttempts, reversals, controlTime "1:42"). PRIDE's first round is 10 minutes. The archives are
|
|
361
|
+
older than the Free/Starter history window: on those plans rounds come back as a HISTORY_WINDOW_EXCEEDED partial
|
|
362
|
+
note while the result still returns.
|
|
363
|
+
|
|
364
|
+
Get boutId from combat_card or combat_athlete (e.g. "raf13--belal-muhammad--colby-covington", "bellator-5600").
|
|
365
|
+
MMA archive bout ids are "<promotion>-<fightId>"; a bare fight id resolves in any MMA archive league.
|
|
366
|
+
|
|
367
|
+
Parallel-safe: yes. API calls: 1–2.
|
|
368
368
|
Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cisneros--ryan-petersen" }`,
|
|
369
369
|
inputSchema: {
|
|
370
370
|
type: 'object',
|
|
@@ -414,7 +414,7 @@ Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cis
|
|
|
414
414
|
source: d.source,
|
|
415
415
|
requestId: d.requestId,
|
|
416
416
|
tookMs: Date.now() - d.started,
|
|
417
|
-
|
|
417
|
+
apiCalls: parts ? 2 : 1,
|
|
418
418
|
rateLimit: bout.headers,
|
|
419
419
|
partial,
|
|
420
420
|
});
|
|
@@ -422,24 +422,24 @@ Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cis
|
|
|
422
422
|
};
|
|
423
423
|
export const combatAthlete = {
|
|
424
424
|
name: 'combat_athlete',
|
|
425
|
-
description: `COMPOSITE RAF athlete / BKFC or MMA archive fighter page: profile + recent bouts + career stats.
|
|
426
|
-
|
|
427
|
-
When to use:
|
|
428
|
-
- "Kyle Snyder's RAF record", "Arlovski BKFC stats", "Tito Ortiz in Bellator", "Fedor's PRIDE record", athlete/fighter profile pages
|
|
429
|
-
|
|
430
|
-
athlete accepts a slug ("kyle-snyder") or a name ("Andrei Arlovski"); names are resolved through the league's
|
|
431
|
-
search, and several matches return AMBIGUOUS_ENTITY with candidates. Profile carries record, division, nickname,
|
|
432
|
-
country, official headshot (+ its credit — show it with the image). Career stats need the Starter plan; on Free
|
|
433
|
-
they come back as a partial note while profile and history still return.
|
|
434
|
-
MMA archives: profile is bio (DOB, height, reach, stance, hometown) + the record in that promotion's archive (not the
|
|
435
|
-
whole career); stats are career totals in UFC's format. league "mma" covers every archived promotion at once, and
|
|
436
|
-
data.recordByPromotion splits the record ({ pride: {...}, strikeforce: {...} }). A fighter we also hold a UFC record
|
|
437
|
-
for keeps the SAME slug as /ufc/fighters/{slug}: data.ufcFighterSlug is set then (a UFC slug is accepted here too).
|
|
438
|
-
Keep the records apart: archive fights never belong in a UFC answer.
|
|
439
|
-
|
|
440
|
-
Prefer over: call_api profile + history + stats separately.
|
|
441
|
-
|
|
442
|
-
Parallel-safe: yes. API calls: 2–4.
|
|
425
|
+
description: `COMPOSITE RAF athlete / BKFC or MMA archive fighter page: profile + recent bouts + career stats.
|
|
426
|
+
|
|
427
|
+
When to use:
|
|
428
|
+
- "Kyle Snyder's RAF record", "Arlovski BKFC stats", "Tito Ortiz in Bellator", "Fedor's PRIDE record", athlete/fighter profile pages
|
|
429
|
+
|
|
430
|
+
athlete accepts a slug ("kyle-snyder") or a name ("Andrei Arlovski"); names are resolved through the league's
|
|
431
|
+
search, and several matches return AMBIGUOUS_ENTITY with candidates. Profile carries record, division, nickname,
|
|
432
|
+
country, official headshot (+ its credit — show it with the image). Career stats need the Starter plan; on Free
|
|
433
|
+
they come back as a partial note while profile and history still return.
|
|
434
|
+
MMA archives: profile is bio (DOB, height, reach, stance, hometown) + the record in that promotion's archive (not the
|
|
435
|
+
whole career); stats are career totals in UFC's format. league "mma" covers every archived promotion at once, and
|
|
436
|
+
data.recordByPromotion splits the record ({ pride: {...}, strikeforce: {...} }). A fighter we also hold a UFC record
|
|
437
|
+
for keeps the SAME slug as /ufc/fighters/{slug}: data.ufcFighterSlug is set then (a UFC slug is accepted here too).
|
|
438
|
+
Keep the records apart: archive fights never belong in a UFC answer.
|
|
439
|
+
|
|
440
|
+
Prefer over: call_api profile + history + stats separately.
|
|
441
|
+
|
|
442
|
+
Parallel-safe: yes. API calls: 2–4.
|
|
443
443
|
Example: { "league": "raf", "athlete": "Kyle Snyder", "historyLimit": 5 }`,
|
|
444
444
|
inputSchema: {
|
|
445
445
|
type: 'object',
|
|
@@ -502,7 +502,7 @@ Example: { "league": "raf", "athlete": "Kyle Snyder", "historyLimit": 5 }`,
|
|
|
502
502
|
source: d.source,
|
|
503
503
|
requestId: d.requestId,
|
|
504
504
|
tookMs: Date.now() - d.started,
|
|
505
|
-
|
|
505
|
+
apiCalls: 1 + (history ? 1 : 0) + (stats ? 1 : 0),
|
|
506
506
|
rateLimit: profile.headers,
|
|
507
507
|
partial,
|
|
508
508
|
});
|
|
@@ -510,13 +510,13 @@ Example: { "league": "raf", "athlete": "Kyle Snyder", "historyLimit": 5 }`,
|
|
|
510
510
|
};
|
|
511
511
|
export const combatH2h = {
|
|
512
512
|
name: 'combat_h2h',
|
|
513
|
-
description: `Head-to-head between two RAF athletes, two BKFC fighters or two MMA archive fighters: record and every meeting.
|
|
514
|
-
|
|
515
|
-
Accepts slugs or names for both sides (names resolve through the league's search). MMA archive meetings are only
|
|
516
|
-
the ones on that promotion's cards (league "mma": every archived promotion); UFC meetings are a UFC question
|
|
517
|
-
(head_to_head).
|
|
518
|
-
|
|
519
|
-
Parallel-safe: yes. API calls: 1–3.
|
|
513
|
+
description: `Head-to-head between two RAF athletes, two BKFC fighters or two MMA archive fighters: record and every meeting.
|
|
514
|
+
|
|
515
|
+
Accepts slugs or names for both sides (names resolve through the league's search). MMA archive meetings are only
|
|
516
|
+
the ones on that promotion's cards (league "mma": every archived promotion); UFC meetings are a UFC question
|
|
517
|
+
(head_to_head).
|
|
518
|
+
|
|
519
|
+
Parallel-safe: yes. API calls: 1–3.
|
|
520
520
|
Example: { "league": "bkfc", "athleteA": "ryan-petersen", "athleteB": "gregoris-cisneros" }`,
|
|
521
521
|
inputSchema: {
|
|
522
522
|
type: 'object',
|
|
@@ -557,23 +557,23 @@ Example: { "league": "bkfc", "athleteA": "ryan-petersen", "athleteB": "gregoris-
|
|
|
557
557
|
source: d.source,
|
|
558
558
|
requestId: d.requestId,
|
|
559
559
|
tookMs: Date.now() - d.started,
|
|
560
|
-
|
|
560
|
+
apiCalls: 1,
|
|
561
561
|
rateLimit: res.headers,
|
|
562
562
|
});
|
|
563
563
|
},
|
|
564
564
|
};
|
|
565
565
|
export const combatRankings = {
|
|
566
566
|
name: 'combat_rankings',
|
|
567
|
-
description: `Champions and rankings: RAF title holders per division, BKFC official rankings + champions.
|
|
568
|
-
|
|
569
|
-
BKFC: omit division for every division's ranked list (champion, interim champion, ranked fighters), or pass one
|
|
570
|
-
(e.g. "heavyweight", "womens-flyweight"). RAF has no ranked lists; it returns every title and its holder, or one
|
|
571
|
-
division's detail when division is given (e.g. "men-heavyweight").
|
|
572
|
-
The MMA archives (bellator, pride, strikeforce, wec, dream, elitexc, affliction, mma) have no rankings: they return a
|
|
573
|
-
non-retryable NOT_IMPLEMENTED.
|
|
574
|
-
Never answer with UFC rankings instead.
|
|
575
|
-
|
|
576
|
-
Parallel-safe: yes. API calls: 1.
|
|
567
|
+
description: `Champions and rankings: RAF title holders per division, BKFC official rankings + champions.
|
|
568
|
+
|
|
569
|
+
BKFC: omit division for every division's ranked list (champion, interim champion, ranked fighters), or pass one
|
|
570
|
+
(e.g. "heavyweight", "womens-flyweight"). RAF has no ranked lists; it returns every title and its holder, or one
|
|
571
|
+
division's detail when division is given (e.g. "men-heavyweight").
|
|
572
|
+
The MMA archives (bellator, pride, strikeforce, wec, dream, elitexc, affliction, mma) have no rankings: they return a
|
|
573
|
+
non-retryable NOT_IMPLEMENTED.
|
|
574
|
+
Never answer with UFC rankings instead.
|
|
575
|
+
|
|
576
|
+
Parallel-safe: yes. API calls: 1.
|
|
577
577
|
Example: { "league": "bkfc", "division": "heavyweight" }`,
|
|
578
578
|
inputSchema: {
|
|
579
579
|
type: 'object',
|
|
@@ -610,7 +610,7 @@ Example: { "league": "bkfc", "division": "heavyweight" }`,
|
|
|
610
610
|
source: d.source,
|
|
611
611
|
requestId: d.requestId,
|
|
612
612
|
tookMs: Date.now() - d.started,
|
|
613
|
-
|
|
613
|
+
apiCalls: 1,
|
|
614
614
|
rateLimit: res.headers,
|
|
615
615
|
});
|
|
616
616
|
},
|