cito-mcp 0.4.9 → 0.5.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/README.md CHANGED
@@ -4,7 +4,7 @@ Standalone [MCP](https://modelcontextprotocol.io) server for the [Cito esports A
4
4
 
5
5
  **Version:** `0.4.9` · **Node:** `>=20` · **Install:** `npx cito-mcp`
6
6
 
7
- Primary games & sports: **lol · cs2 · dota2 · cod · ufc · tennis**, plus **RAF wrestling**, **BKFC bare knuckle** and the **Bellator MMA archive** (2014-2017) through the `combat_*` tools. Long-tail REST stays available via `call_api`.
7
+ Primary games & sports: **lol · cs2 · dota2 · cod · ufc · tennis**, plus **RAF wrestling**, **BKFC bare knuckle** and the **non-UFC MMA archives** (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction, and `mma` across all of them) through the `combat_*` tools. Long-tail REST stays available via `call_api`.
8
8
 
9
9
  ---
10
10
 
@@ -244,13 +244,13 @@ upstream, so a non-null `headshotUrl` always has a `license` beside it.
244
244
 
245
245
  ### Combat leagues (`combat_*`, `league` argument)
246
246
 
247
- RAF, BKFC and Bellator are not on the `game` enum. `combat_events`, `combat_card`, `combat_bout`, `combat_athlete`, `combat_h2h` and `combat_rankings` take `league: "raf" | "bkfc" | "bellator"`, and `call_api` reaches `/raf`, `/bkfc` and `/bellator`. None of them is ever mixed into UFC answers.
247
+ RAF, BKFC and the MMA archives are not on the `game` enum. `combat_events`, `combat_card`, `combat_bout`, `combat_athlete`, `combat_h2h` and `combat_rankings` take `league: "raf" | "bkfc" | "bellator" | "pride" | "strikeforce" | "wec" | "dream" | "elitexc" | "affliction" | "mma"`, and `call_api` reaches each of those prefixes. None of them is ever mixed into UFC answers.
248
248
 
249
249
  | League | Scoring | Notes |
250
250
  | --- | --- | --- |
251
251
  | `raf` | per PERIOD (wrestling points, takedowns) | Athletes; champions per division |
252
252
  | `bkfc` | per ROUND (punches landed/thrown, knockdowns) | Fighters; official rankings + champions |
253
- | `bellator` | per ROUND, official stats in UFC's shape (`"13 of 18"`, `controlTime "1:42"`, round 0 = whole fight; knockdowns, submission attempts, reversals) | Closed archive: Bellator 131-172 (Nov 2014 - Feb 2017), ~20 cards / ~230 bouts. Bout ids like `bellator-5600`, event slugs like `bellator-142` (or `142`). A fighter we also hold a UFC record for keeps the **same slug** as `/ufc/fighters/{slug}` (`ufcFighterSlug`). No live data, rankings or upcoming cards: `combat_events` `when:"upcoming"` returns `[]` with a note (default is `recent`), `combat_card` `event:"last"` is the most recent card (the default) and `"next"` is a non-retryable `NOT_IMPLEMENTED`, `combat_rankings` is a non-retryable `NOT_IMPLEMENTED`. All of it is older than the Free (30-day) / Starter (90-day) history window, so its stats are Pro: on those plans round stats come back as a `HISTORY_WINDOW_EXCEEDED` partial note while results still return. |
253
+ | MMA archives: `bellator`, `pride`, `strikeforce`, `wec`, `dream`, `elitexc`, `affliction`, `mma` | per ROUND, official stats in UFC's shape (`"13 of 18"`, `controlTime "1:42"`, round 0 = whole fight; knockdowns, submission attempts, reversals) | Closed archives on one API router with identical routes: Bellator 131-172 (2014-2017), PRIDE 1997-2007 (PRIDE rules: 10-minute first round, then 5-minute rounds), Strikeforce 2006-2013, WEC 2001-2010, DREAM 2008-2012, EliteXC 2007-2008, Affliction 2008-2009. `mma` is every promotion at once: a fighter's whole non-UFC career, with `recordByPromotion` on `combat_athlete`, and `recent` / `last` across promotions; every bout and event carries its real `promotion`. Event slugs like `bellator-142`, `pride-34`, `wec-48`, `strikeforce-barnett-vs-cormier`; bout ids `<promotion>-<fightId>` (a bare fight id resolves in any archive). A fighter we also hold a UFC record for keeps the **same slug** as `/ufc/fighters/{slug}` (`ufcFighterSlug`). No live data, rankings or upcoming cards: `combat_events` `when:"upcoming"` returns `[]` with a note (default is `recent`), `combat_card` `event:"last"` is the most recent card (the default) and `"next"` is a non-retryable `NOT_IMPLEMENTED`, `combat_rankings` is a non-retryable `NOT_IMPLEMENTED`. All of it is older than the Free (30-day) / Starter (90-day) history window, so its stats are Pro: on those plans round stats come back as a `HISTORY_WINDOW_EXCEEDED` partial note while results still return. |
254
254
 
255
255
  ### UFC projection notes (0.2.4)
256
256
 
@@ -269,7 +269,7 @@ Fortnite: **`call_api` only** until promoted into the primary enum.
269
269
 
270
270
  Path must start with `/` and match one of:
271
271
 
272
- `/health` · `/lol` · `/cs2` · `/dota2` · `/cod` · `/ufc` · `/fortnite` · `/tennis` · `/raf` · `/bkfc` · `/bellator` · `/openapi.json`
272
+ `/health` · `/lol` · `/cs2` · `/dota2` · `/cod` · `/ufc` · `/fortnite` · `/tennis` · `/raf` · `/bkfc` · `/bellator` · `/pride` · `/strikeforce` · `/wec` · `/dream` · `/elitexc` · `/affliction` · `/mma` · `/openapi.json`
273
273
 
274
274
  Absolute URLs and `..` traversal → `PATH_NOT_ALLOWED`. Prefer curated tools for all standard jobs.
275
275
 
@@ -552,8 +552,8 @@ claude mcp add cito -e CITO_API_KEY=cito_… "--" npx -y cito-mcp
552
552
 
553
553
  ### 0.4.9
554
554
 
555
- - **Bellator MMA archive** (`/api/v1/bellator`): `league: "bellator"` on every `combat_*` tool, and `/bellator` on the `call_api` / `list_routes` allowlist. Bellator 131-172 (Nov 2014 - Feb 2017) with official per-round stats in `/ufc`'s shape; fighters with a UFC record share the UFC slug (`combat_athlete` returns `ufcFighterSlug`). Archive gaps are answered honestly: upcoming events are an empty list with a note, `combat_card` defaults to the most recent card and refuses `"next"`, and `combat_rankings` returns a non-retryable `NOT_IMPLEMENTED`. `HISTORY_WINDOW_EXCEEDED` sections are reported as a plan gap (Pro), not as a key problem.
556
- - Agent guidance (server instructions, `list_capabilities`): Bellator is never mixed into UFC answers; UFC already includes Dana White's Contender Series and **Road to UFC** (2022-2026, all 19 cards with official round stats, slugs like `road-to-ufc-2-7`).
555
+ - **Non-UFC MMA archives**: `league` `bellator` (2014-2017), `pride` (1997-2007), `strikeforce` (2006-2013), `wec` (2001-2010), `dream` (2008-2012), `elitexc` (2007-2008), `affliction` (2008-2009) and `mma` (all of them at once) on every `combat_*` tool, and `/bellator` `/pride` `/strikeforce` `/wec` `/dream` `/elitexc` `/affliction` `/mma` on the `call_api` / `list_routes` allowlist. Official per-round stats in `/ufc`'s shape; fighters with a UFC record share the UFC slug (`combat_athlete` returns `ufcFighterSlug`, and under `mma` the whole non-UFC career split by `recordByPromotion`); `mma` events `recent` and `combat_card` `last` work across promotions. Archive gaps are answered honestly: upcoming events are an empty list with a note, `combat_card` defaults to the most recent card and refuses `"next"`, and `combat_rankings` returns a non-retryable `NOT_IMPLEMENTED`. `HISTORY_WINDOW_EXCEEDED` sections are reported as a plan gap (Pro), not as a key problem.
556
+ - Agent guidance (server instructions, `list_capabilities`): the MMA archives are never mixed into UFC answers; UFC already includes Dana White's Contender Series and **Road to UFC** (2022-2026, all 19 cards with official round stats, slugs like `road-to-ufc-2-7`).
557
557
 
558
558
  ### 0.4.8
559
559
 
package/dist/envelope.js CHANGED
@@ -38,7 +38,7 @@ export const DEFAULT_RECOVER = {
38
38
  'Call api_health to see which games your plan includes',
39
39
  ],
40
40
  PATH_NOT_ALLOWED: [
41
- 'Use only allowlisted path prefixes: /health /lol /cs2 /dota2 /cod /ufc /fortnite /tennis /raf /bkfc /bellator',
41
+ 'Use only allowlisted path prefixes: /health /lol /cs2 /dota2 /cod /ufc /fortnite /tennis /raf /bkfc, MMA archives /bellator /pride /strikeforce /wec /dream /elitexc /affliction /pfl /oktagon /mma',
42
42
  'Prefer a curated tool from list_capabilities when one covers the outcome',
43
43
  ],
44
44
  NOT_IMPLEMENTED: [
@@ -18,7 +18,9 @@ game enum (unless a tool documents otherwise): lol | cs2 | dota2 | ufc | cod | t
18
18
  - On UNSUPPORTED_GAME / 403 for a title: stop retrying that game; call api_health; report plan gaps.
19
19
  - Fortnite and other titles may appear via call_api/resources; do not assume they are in the curated enum until list_capabilities says so.
20
20
  - RAF (Real American Freestyle wrestling) and BKFC (Bare Knuckle Fighting Championship) are NOT on the game enum: use the combat_* tools with league raf|bkfc (combat_events, combat_card, combat_bout, combat_athlete, combat_h2h, combat_rankings), or call_api /raf/... and /bkfc/.... RAF scores per PERIOD (wrestling points, takedowns); BKFC per ROUND (punches landed/thrown, knockdowns). Never mix either into UFC answers.
21
- - Bellator MMA is not on the enum either: combat_* tools with league bellator, or call_api /bellator/.... A closed 2014-2017 archive (Bellator 131-172) with official per-ROUND stats in UFC's shape ("13 of 18", round 0 = whole fight); no live, rankings or upcoming cards (combat_card "last" = most recent card). A fighter with a UFC record keeps the SAME slug as /ufc/fighters/{slug} (ufcFighterSlug). Never mix Bellator fights into UFC answers. Its stats predate the Free/Starter window: HISTORY_WINDOW_EXCEEDED means Pro, not a bad key.
21
+ - MMA archives: Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction, plus /mma for a fighter's whole non-UFC career (recordByPromotion). combat_* tools with league bellator|pride|strikeforce|wec|dream|elitexc|affliction|mma, or call_api /{league}/.... Closed archives (1997-2017) with official per-ROUND stats in UFC's shape ("13 of 18", round 0 = whole fight); no live, rankings or upcoming cards (combat_card "last" = most recent card). Fighters share their UFC slug (ufcFighterSlug). Never mix these into UFC answers. Their stats predate the Free/Starter window: HISTORY_WINDOW_EXCEEDED means Pro, not a bad key.
22
+ - PFL (Professional Fighters League): combat_* with league pfl, or call_api /pfl/.... Every card since 2018, upcoming cards (combat_events when upcoming, combat_card "next") and live fight status (/pfl/live: bout status "live" with live.round / live.clock). Results only (winner, method, round, time): no strike or round stats exist for PFL. league mma includes PFL. Never mix into UFC answers.
23
+ - OKTAGON MMA: combat_* with league oktagon, or call_api /oktagon/.... Every card since Oktagon 1 (2016), upcoming cards and live fight status. Per-ROUND stats on most bouts, LANDED ONLY: significant/total strikes, head/body/leg and takedowns as plain counts (landed.*), plus submission attempts; attempts, accuracy, knockdowns and control time are null because the source does not log them (each bout's statsCoverage says which fields exist). Fighters share their UFC slug when proven. league mma includes OKTAGON. Never mix into UFC answers.
22
24
  - UFC includes DWCS and Road to UFC (2022-2026, all 19 cards with official round stats; slugs like road-to-ufc-2-7, "Road to UFC 2.7").
23
25
 
24
26
  ## Preferred tool order
@@ -1,22 +1,46 @@
1
1
  import { asRecord, clampInt, extractRows, fetchJson, gameNotIncludedHint } from '../client.js';
2
2
  import { errorEnvelope, mapHttpToCode, newRequestId, partialFromRejection, successEnvelope, } from '../envelope.js';
3
3
  import { boolSchema, limitSchema, stringSchema } from './types.js';
4
- export const COMBAT_LEAGUES = ['raf', 'bkfc', 'bellator'];
4
+ export const MMA_ARCHIVE_LEAGUES = ['bellator', 'pride', 'strikeforce', 'wec', 'dream', 'elitexc', 'affliction'];
5
+ export const MMA_LEAGUES = [...MMA_ARCHIVE_LEAGUES, 'pfl', 'oktagon', 'mma'];
6
+ export const COMBAT_LEAGUES = ['raf', 'bkfc', ...MMA_LEAGUES];
7
+ const isMma = (league) => MMA_LEAGUES.includes(league);
5
8
  const LEAGUE_NAMES = {
6
9
  raf: 'Real American Freestyle (pro freestyle wrestling)',
7
10
  bkfc: 'Bare Knuckle Fighting Championship',
8
11
  bellator: 'Bellator MMA',
12
+ pride: 'PRIDE Fighting Championships',
13
+ strikeforce: 'Strikeforce',
14
+ wec: 'World Extreme Cagefighting',
15
+ dream: 'DREAM',
16
+ elitexc: 'EliteXC',
17
+ affliction: 'Affliction Entertainment',
18
+ pfl: 'Professional Fighters League',
19
+ oktagon: 'OKTAGON MMA',
20
+ mma: 'MMA beyond the UFC (every non-UFC promotion we hold, PFL and OKTAGON included)',
9
21
  };
10
22
  const ARCHIVES = {
11
- bellator: 'Bellator is a closed archive (Bellator 131-172, November 2014 to February 2017)',
23
+ bellator: { short: 'Bellator', note: 'Bellator is a closed archive (2014-2024; official round stats for Bellator 131-172, results only after that)' },
24
+ pride: { short: 'PRIDE', note: 'PRIDE is a closed archive (1997-2007; PRIDE rules: a 10-minute first round, then 5-minute rounds)' },
25
+ strikeforce: { short: 'Strikeforce', note: 'Strikeforce is a closed archive (2006-2013)' },
26
+ wec: { short: 'WEC', note: 'WEC is a closed archive (2001-2010, until it merged into the UFC)' },
27
+ dream: { short: 'DREAM', note: 'DREAM is a closed archive (2008-2012)' },
28
+ elitexc: { short: 'EliteXC', note: 'EliteXC is a closed archive (2007-2008)' },
29
+ affliction: { short: 'Affliction', note: 'Affliction is a closed archive (2008-2009)' },
12
30
  };
13
- const PEOPLE = { raf: 'athletes', bkfc: 'fighters', bellator: 'fighters' };
14
- const HISTORY = { raf: 'bouts', bkfc: 'fights', bellator: 'fights' };
31
+ function archiveOf(league) {
32
+ return ARCHIVES[league];
33
+ }
34
+ const PEOPLE = (league) => (league === 'raf' ? 'athletes' : 'fighters');
35
+ const HISTORY = (league) => (league === 'raf' ? 'bouts' : 'fights');
15
36
  const leagueSchema = {
16
37
  type: 'string',
17
38
  enum: [...COMBAT_LEAGUES],
18
- description: 'League: "raf" (Real American Freestyle wrestling), "bkfc" (Bare Knuckle Fighting Championship) or "bellator" ' +
19
- '(Bellator MMA archive, 2014-2017). Example: "bkfc".',
39
+ description: 'League: "raf" (Real American Freestyle wrestling), "bkfc" (Bare Knuckle Fighting Championship), or an MMA archive: ' +
40
+ '"bellator" (2014-2017), "pride" (1997-2007), "strikeforce" (2006-2013), "wec" (2001-2010), "dream" (2008-2012), ' +
41
+ '"elitexc" (2007-2008), "affliction" (2008-2009), "pfl" (PFL, 2018 to now: upcoming cards, live fight status, results only), ' +
42
+ '"oktagon" (OKTAGON MMA, 2016 to now: upcoming cards, live fight status, per-round LANDED strikes by target and takedowns), ' +
43
+ 'or "mma" (all of them at once). Example: "bkfc".',
20
44
  };
21
45
  function parseLeague(value) {
22
46
  const v = typeof value === 'string' ? value.trim().toLowerCase() : '';
@@ -25,7 +49,7 @@ function parseLeague(value) {
25
49
  function leagueError(d, value) {
26
50
  return errorEnvelope({
27
51
  code: 'VALIDATION',
28
- message: `league must be raf, bkfc or bellator (got ${JSON.stringify(value ?? null)})`,
52
+ message: `league must be one of ${COMBAT_LEAGUES.join(', ')} (got ${JSON.stringify(value ?? null)})`,
29
53
  game: null,
30
54
  source: d.source,
31
55
  requestId: d.requestId,
@@ -75,7 +99,7 @@ function sectionFailure(section, res, league) {
75
99
  if (isHistoryWindow(res)) {
76
100
  message =
77
101
  `${section} is older than this key's history window (HISTORY_WINDOW_EXCEEDED: Free 30 days, Starter 90 days; ` +
78
- `Pro reads the full archive)` + (league && ARCHIVES[league] ? `. ${ARCHIVES[league]}, so all of its stats need Pro` : '');
102
+ `Pro reads the full archive)` + (league && archiveOf(league) ? `. ${archiveOf(league).note}, so all of its stats need Pro` : '');
79
103
  }
80
104
  else if (res.status === 403) {
81
105
  message = `${section} needs the Starter plan or above for this league (${err?.message || 'plan restriction'})`;
@@ -98,7 +122,7 @@ function archiveGap(d, league, message, recover, calls = 0) {
98
122
  async function resolvePerson(ctx, league, input) {
99
123
  const raw = input.trim();
100
124
  if (/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(raw)) {
101
- const probe = await fetchJson(ctx, `/${league}/${PEOPLE[league]}/${encodeURIComponent(raw)}`);
125
+ const probe = await fetchJson(ctx, `/${league}/${PEOPLE(league)}/${encodeURIComponent(raw)}`);
102
126
  if (probe.ok)
103
127
  return { slug: raw };
104
128
  if (probe.status !== 404)
@@ -129,7 +153,7 @@ async function resolveOrFail(d, ctx, league, input, argName) {
129
153
  return {
130
154
  fail: errorEnvelope({
131
155
  code: 'AMBIGUOUS_ENTITY',
132
- message: `"${input}" matches several ${PEOPLE[league]}; pass one slug from details.candidates`,
156
+ message: `"${input}" matches several ${PEOPLE(league)}; pass one slug from details.candidates`,
133
157
  game: league,
134
158
  source: d.source,
135
159
  requestId: d.requestId,
@@ -141,7 +165,7 @@ async function resolveOrFail(d, ctx, league, input, argName) {
141
165
  return {
142
166
  fail: errorEnvelope({
143
167
  code: 'NOT_FOUND',
144
- message: `No ${league.toUpperCase()} ${PEOPLE[league].slice(0, -1)} matches "${input}"`,
168
+ message: `No ${league.toUpperCase()} ${PEOPLE(league).slice(0, -1)} matches "${input}"`,
145
169
  game: league,
146
170
  source: d.source,
147
171
  requestId: d.requestId,
@@ -154,17 +178,18 @@ function begin(source) {
154
178
  }
155
179
  export const combatEvents = {
156
180
  name: 'combat_events',
157
- description: `RAF wrestling, BKFC bare-knuckle or Bellator MMA events: upcoming cards or recent results.
181
+ description: `RAF wrestling, BKFC bare-knuckle or MMA archive (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction) events.
158
182
 
159
183
  When to use:
160
- - "When is the next RAF / BKFC card?", "recent BKFC results", "Bellator cards from 2015", schedule pages
184
+ - "When is the next RAF / BKFC card?", "recent BKFC results", "PRIDE results", "Bellator cards from 2015", schedule pages
161
185
  - Getting an event slug for combat_card
162
186
 
163
187
  Each event carries slug, name, date, local start time + timezone, venue/location, status and bout counts.
164
- Bellator is a closed archive (Bellator 131-172, Nov 2014 - Feb 2017): "when" defaults to "recent" (newest card
165
- first), and "upcoming" returns an empty list with data.note instead of failing.
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.
166
191
 
167
- Prefer over: call_api /raf/events, /bkfc/events or /bellator/events.
192
+ Prefer over: call_api /{league}/events.
168
193
  Do not use when: you already have the event slug and want its bouts (combat_card).
169
194
 
170
195
  Parallel-safe: yes. Upstream cost: 1.
@@ -180,7 +205,7 @@ Example: { "league": "bkfc", "when": "upcoming", "limit": 5 }`,
180
205
  enum: ['upcoming', 'recent'],
181
206
  default: 'upcoming',
182
207
  description: 'upcoming = scheduled cards, soonest first; recent = completed cards, newest first. Default "upcoming" ' +
183
- '("recent" for bellator, an archive). Example: "recent".',
208
+ '("recent" for the MMA archives). Example: "recent".',
184
209
  },
185
210
  limit: limitSchema({ default: 10, max: 50, description: 'Max events (default 10, max 50).' }),
186
211
  },
@@ -190,14 +215,14 @@ Example: { "league": "bkfc", "when": "upcoming", "limit": 5 }`,
190
215
  const league = parseLeague(args.league);
191
216
  if (!league)
192
217
  return leagueError(d, args.league);
193
- const archive = ARCHIVES[league];
218
+ const archive = archiveOf(league);
194
219
  const when = args.when === 'recent' || args.when === 'upcoming' ? args.when : archive ? 'recent' : 'upcoming';
195
220
  const limit = clampInt(args.limit, 10, 1, 50);
196
221
  if (archive && when === 'upcoming') {
197
222
  return successEnvelope({
198
223
  data: {
199
224
  league, leagueName: LEAGUE_NAMES[league], when, count: 0, events: [],
200
- note: `${archive}: there are no upcoming cards. Use when "recent" for its cards, newest first.`,
225
+ note: `${archive.note}: there are no upcoming cards. Use when "recent" for its cards, newest first.`,
201
226
  },
202
227
  game: league,
203
228
  source: d.source,
@@ -223,18 +248,18 @@ Example: { "league": "bkfc", "when": "upcoming", "limit": 5 }`,
223
248
  };
224
249
  export const combatCard = {
225
250
  name: 'combat_card',
226
- description: `COMPOSITE fight card for one RAF, BKFC or Bellator event: event identity + bouts in running order with results.
251
+ description: `COMPOSITE fight card for one RAF, BKFC or MMA archive event: event identity + bouts in running order with results.
227
252
 
228
253
  When to use:
229
- - "RAF 14 card", "BKFC 95 results", "Bellator 142 card", "who won the main event at the last BKFC?"
254
+ - "RAF 14 card", "BKFC 95 results", "Bellator 142 card", "PRIDE 34", "who won the main event at the last BKFC?"
230
255
  - Event pages and results pages
231
256
 
232
- event accepts a slug (e.g. "raf13", "bkfc-95-newark", "bellator-142" or "142") or "next" / "last". Bouts come main
233
- event first; each carries weight class, title stakes, both sides (athletes for RAF, red/blue fighters for BKFC with
234
- punch totals, red/blue fighters for Bellator with UFC-format totals and ufcFighterSlug), and once finished the winner
235
- and result (RAF: score, takedowns, method; BKFC/Bellator: method, round, time).
236
- Bellator is an archive: event defaults to "last" (its most recent card, Bellator 172), and "next" is a
237
- non-retryable NOT_IMPLEMENTED (there are no upcoming cards).
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).
238
263
 
239
264
  Prefer over: combat_events + call_api per bout.
240
265
  Do not use when: you need one bout's period/round breakdown (combat_bout).
@@ -247,7 +272,7 @@ Example: { "league": "raf", "event": "last" }`,
247
272
  required: ['league'],
248
273
  properties: {
249
274
  league: leagueSchema,
250
- event: stringSchema('Event slug, or "next" / "last" (default "next"; "last" for bellator). Example: "raf13".', 'next'),
275
+ event: stringSchema('Event slug, or "next" / "last" (default "next"; "last" for the MMA archives). Example: "raf13".', 'next'),
251
276
  limit: limitSchema({ default: 30, max: 40, description: 'Max bouts on the card (default 30, max 40).' }),
252
277
  },
253
278
  },
@@ -257,11 +282,11 @@ Example: { "league": "raf", "event": "last" }`,
257
282
  if (!league)
258
283
  return leagueError(d, args.league);
259
284
  const limit = clampInt(args.limit, 30, 1, 40);
260
- const archive = ARCHIVES[league];
285
+ const archive = archiveOf(league);
261
286
  let slug = typeof args.event === 'string' && args.event.trim() ? args.event.trim().toLowerCase() : archive ? 'last' : 'next';
262
287
  let calls = 0;
263
288
  if (archive && slug === 'next') {
264
- return archiveGap(d, league, `${archive}: there is no next card.`, [
289
+ return archiveGap(d, league, `${archive.note}: there is no next card.`, [
265
290
  `Use event "last" for the most recent ${league} card, or a slug from combat_events { league: "${league}", when: "recent" }`,
266
291
  ]);
267
292
  }
@@ -290,7 +315,7 @@ Example: { "league": "raf", "event": "last" }`,
290
315
  slug = String(first.slug);
291
316
  }
292
317
  const [ev, bouts] = await Promise.all([
293
- fetchJson(ctx, `/${league}/events/${encodeURIComponent(slug)}`, league === 'bellator' ? { query: { includeBouts: 'false' } } : undefined),
318
+ fetchJson(ctx, `/${league}/events/${encodeURIComponent(slug)}`, archive ? { query: { includeBouts: 'false' } } : undefined),
294
319
  fetchJson(ctx, `/${league}/events/${encodeURIComponent(slug)}/bouts`),
295
320
  ]);
296
321
  calls += 2;
@@ -324,18 +349,20 @@ Example: { "league": "raf", "event": "last" }`,
324
349
  };
325
350
  export const combatBout = {
326
351
  name: 'combat_bout',
327
- description: `One RAF, BKFC or Bellator bout in full, with its scoring breakdown.
352
+ description: `One RAF, BKFC or MMA archive bout in full, with its scoring breakdown.
328
353
 
329
354
  RAF: winner, method (decision / tech fall / pin / injury), final score, takedowns, and per-PERIOD scores.
330
355
  BKFC: winner, method, round, time, and per-ROUND punch stats for red and blue (landed/thrown, head/body,
331
356
  distance/clinch, knockdowns). BKFC round stats need the Starter plan; on Free they come back as a partial note.
332
- Bellator: winner, method, round, time, finish (position / submission), referee, judges' scores, each corner's
333
- whole-fight totals, and per-ROUND official stats in UFC's format (significantStrikes "13 of 18", totalStrikes,
334
- takedowns, head/body/leg, distance/clinch/ground, knockdowns, submissionAttempts, reversals, controlTime "1:42").
335
- The archive is 2014-2017, older than the Free/Starter history window: on those plans rounds come back as a
336
- HISTORY_WINDOW_EXCEEDED partial note while the result still returns.
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.
337
363
 
338
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.
339
366
 
340
367
  Parallel-safe: yes. Upstream cost: 1–2.
341
368
  Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cisneros--ryan-petersen" }`,
@@ -345,8 +372,8 @@ Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cis
345
372
  required: ['league', 'boutId'],
346
373
  properties: {
347
374
  league: leagueSchema,
348
- boutId: stringSchema('Bout id from combat_card / combat_athlete (Bellator: "bellator-5600"). Example: "raf13--belal-muhammad--colby-covington".'),
349
- includeBreakdown: boolSchema('Include per-period (RAF) / per-round (BKFC, Bellator) breakdown (default true).', true),
375
+ boutId: stringSchema('Bout id from combat_card / combat_athlete (MMA archives: "<promotion>-<fightId>"). Example: "raf13--belal-muhammad--colby-covington".'),
376
+ includeBreakdown: boolSchema('Include per-period (RAF) / per-round (BKFC, MMA archives) breakdown (default true).', true),
350
377
  },
351
378
  },
352
379
  handler: async (args, ctx) => {
@@ -379,7 +406,7 @@ Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cis
379
406
  const b = asRecord(data.bout);
380
407
  if (league === 'bkfc' && b && 'rounds' in b && data.rounds)
381
408
  delete b.rounds;
382
- if (league === 'bellator' && b && 'roundStats' in b && (data.rounds || !breakdown))
409
+ if (archiveOf(league) && b && 'roundStats' in b && (data.rounds || !breakdown))
383
410
  delete b.roundStats;
384
411
  return successEnvelope({
385
412
  data,
@@ -395,19 +422,20 @@ Example: { "league": "bkfc", "boutId": "bkfc-fight-night-bethlehem--gregoris-cis
395
422
  };
396
423
  export const combatAthlete = {
397
424
  name: 'combat_athlete',
398
- description: `COMPOSITE RAF athlete / BKFC or Bellator fighter page: profile + recent bouts + career stats.
425
+ description: `COMPOSITE RAF athlete / BKFC or MMA archive fighter page: profile + recent bouts + career stats.
399
426
 
400
427
  When to use:
401
- - "Kyle Snyder's RAF record", "Arlovski BKFC stats", "Tito Ortiz in Bellator", athlete/fighter profile pages
428
+ - "Kyle Snyder's RAF record", "Arlovski BKFC stats", "Tito Ortiz in Bellator", "Fedor's PRIDE record", athlete/fighter profile pages
402
429
 
403
430
  athlete accepts a slug ("kyle-snyder") or a name ("Andrei Arlovski"); names are resolved through the league's
404
431
  search, and several matches return AMBIGUOUS_ENTITY with candidates. Profile carries record, division, nickname,
405
432
  country, official headshot (+ its credit — show it with the image). Career stats need the Starter plan; on Free
406
433
  they come back as a partial note while profile and history still return.
407
- Bellator: profile is bio (DOB, height, reach, stance, hometown) + Bellator-only record (2014-2017 archive, not the
408
- whole career); stats are career totals in UFC's format. A fighter we also hold a UFC record for keeps the SAME slug
409
- as /ufc/fighters/{slug}: data.ufcFighterSlug is set then (a UFC slug is accepted here too). Keep the two records
410
- apart: Bellator fights never belong in a UFC answer.
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.
411
439
 
412
440
  Prefer over: call_api profile + history + stats separately.
413
441
 
@@ -438,20 +466,23 @@ Example: { "league": "raf", "athlete": "Kyle Snyder", "historyLimit": 5 }`,
438
466
  const r = await resolveOrFail(d, ctx, league, input, 'athlete');
439
467
  if ('fail' in r)
440
468
  return r.fail;
441
- const base = `/${league}/${PEOPLE[league]}/${encodeURIComponent(r.slug)}`;
469
+ const base = `/${league}/${PEOPLE(league)}/${encodeURIComponent(r.slug)}`;
442
470
  const historyLimit = clampInt(args.historyLimit, 10, 1, 50);
443
471
  const [profile, history, stats] = await Promise.all([
444
472
  fetchJson(ctx, base),
445
- args.includeHistory === false ? Promise.resolve(null) : fetchJson(ctx, `${base}/${HISTORY[league]}`, { query: { limit: historyLimit } }),
473
+ args.includeHistory === false ? Promise.resolve(null) : fetchJson(ctx, `${base}/${HISTORY(league)}`, { query: { limit: historyLimit } }),
446
474
  args.includeStats === false ? Promise.resolve(null) : fetchJson(ctx, `${base}/stats`),
447
475
  ]);
448
476
  if (!profile.ok)
449
- return httpError(d, league, profile, `${league} ${PEOPLE[league]} ${r.slug}`);
477
+ return httpError(d, league, profile, `${league} ${PEOPLE(league)} ${r.slug}`);
450
478
  const partial = [];
451
479
  const data = { league, slug: r.slug, profile: payload(profile) };
452
- if (league === 'bellator') {
480
+ if (isMma(league)) {
453
481
  const ufc = asRecord(asRecord(data.profile)?.ufc);
454
482
  data.ufcFighterSlug = typeof ufc?.ufcFighterSlug === 'string' ? ufc.ufcFighterSlug : null;
483
+ const byPromotion = asRecord(data.profile)?.recordByPromotion;
484
+ if (league === 'mma' || byPromotion)
485
+ data.recordByPromotion = asRecord(byPromotion) ?? null;
455
486
  }
456
487
  if (history) {
457
488
  if (history.ok)
@@ -479,10 +510,11 @@ Example: { "league": "raf", "athlete": "Kyle Snyder", "historyLimit": 5 }`,
479
510
  };
480
511
  export const combatH2h = {
481
512
  name: 'combat_h2h',
482
- description: `Head-to-head between two RAF athletes, two BKFC fighters or two Bellator fighters: record and every meeting.
513
+ description: `Head-to-head between two RAF athletes, two BKFC fighters or two MMA archive fighters: record and every meeting.
483
514
 
484
- Accepts slugs or names for both sides (names resolve through the league's search). Bellator meetings are only
485
- the ones on Bellator cards (2014-2017); UFC meetings between the same two are a UFC question (head_to_head).
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).
486
518
 
487
519
  Parallel-safe: yes. Upstream cost: 1–3.
488
520
  Example: { "league": "bkfc", "athleteA": "ryan-petersen", "athleteB": "gregoris-cisneros" }`,
@@ -537,7 +569,8 @@ export const combatRankings = {
537
569
  BKFC: omit division for every division's ranked list (champion, interim champion, ranked fighters), or pass one
538
570
  (e.g. "heavyweight", "womens-flyweight"). RAF has no ranked lists; it returns every title and its holder, or one
539
571
  division's detail when division is given (e.g. "men-heavyweight").
540
- Bellator has no rankings (a 2014-2017 archive): league "bellator" returns a non-retryable NOT_IMPLEMENTED.
572
+ The MMA archives (bellator, pride, strikeforce, wec, dream, elitexc, affliction, mma) have no rankings: they return a
573
+ non-retryable NOT_IMPLEMENTED.
541
574
  Never answer with UFC rankings instead.
542
575
 
543
576
  Parallel-safe: yes. Upstream cost: 1.
@@ -556,11 +589,12 @@ Example: { "league": "bkfc", "division": "heavyweight" }`,
556
589
  const league = parseLeague(args.league);
557
590
  if (!league)
558
591
  return leagueError(d, args.league);
559
- if (league === 'bellator') {
560
- return archiveGap(d, league, `Bellator has no rankings or champions lists: ${ARCHIVES.bellator}, and the API serves its results only.`, [
561
- 'Do not retry: no Bellator rankings exist on the API',
562
- 'Title fights: call_api /bellator/bouts with query { "title": "true" }; results: combat_events { league: "bellator", when: "recent" }',
563
- 'Never substitute UFC rankings for Bellator',
592
+ const archive = archiveOf(league) ?? (isMma(league) ? { short: league === 'pfl' ? 'PFL' : league === 'oktagon' ? 'OKTAGON' : 'The MMA namespace', note: 'the API carries its cards and results' } : undefined);
593
+ if (archive) {
594
+ return archiveGap(d, league, `${archive.short} has no rankings or champions lists: ${archive.note}, and the API serves its results only.`, [
595
+ `Do not retry: no ${league} rankings exist on the API`,
596
+ `Title fights: call_api /${league}/bouts with query { "title": "true" }; results: combat_events { league: "${league}", when: "recent" }`,
597
+ `Never substitute UFC rankings for ${archive.short}`,
564
598
  ]);
565
599
  }
566
600
  const division = typeof args.division === 'string' ? args.division.trim().toLowerCase() : '';
@@ -2,23 +2,24 @@ import { extractRows, fetchJson, gameNotIncludedHint, present } from '../client.
2
2
  import { errorEnvelope, mapHttpToCode, newRequestId, successEnvelope, } from '../envelope.js';
3
3
  import { boolSchema, gameSchema, parseGame, PRIMARY_GAMES, stringSchema, } from './types.js';
4
4
  import { PACKAGE_VERSION as CATALOG_VERSION } from '../version.js';
5
+ import { COMBAT_LEAGUES } from './combat.js';
5
6
  const COMBAT_CATALOG = [
6
7
  {
7
8
  name: 'combat_events',
8
9
  doNotUse: 'You already have the event slug → combat_card',
9
- outcome: 'RAF wrestling / BKFC bare-knuckle / Bellator MMA events: upcoming cards or recent results, with date, local time and venue (Bellator: archive, upcoming is empty with a note)',
10
+ outcome: 'RAF wrestling / BKFC bare-knuckle / MMA archive (Bellator, PRIDE, Strikeforce, WEC, DREAM, EliteXC, Affliction, mma = all) events: upcoming cards or recent results, with date, local time and venue (archives: upcoming is empty with a note)',
10
11
  parallelSafe: true,
11
- games: ['raf', 'bkfc', 'bellator'],
12
+ games: [...COMBAT_LEAGUES],
12
13
  jobs: ['schedule'],
13
14
  exampleArgs: { league: 'bkfc', when: 'upcoming', limit: 5 },
14
- preferOver: ['call_api /raf/events, /bkfc/events or /bellator/events'],
15
+ preferOver: ['call_api /{league}/events'],
15
16
  },
16
17
  {
17
18
  name: 'combat_card',
18
19
  doNotUse: "One bout's period/round breakdown → combat_bout",
19
- outcome: 'One RAF/BKFC/Bellator event card in running order with results; event = slug, "next" or "last" (Bellator: "last" = most recent archived card, no "next")',
20
+ outcome: 'One RAF/BKFC/MMA archive event card in running order with results; event = slug, "next" or "last" (archives: "last" = most recent card, across promotions for mma; no "next")',
20
21
  parallelSafe: true,
21
- games: ['raf', 'bkfc', 'bellator'],
22
+ games: [...COMBAT_LEAGUES],
22
23
  jobs: ['event_card', 'schedule', 'media'],
23
24
  exampleArgs: { league: 'raf', event: 'last' },
24
25
  preferOver: ['combat_events + call_api per bout'],
@@ -27,9 +28,9 @@ const COMBAT_CATALOG = [
27
28
  name: 'combat_bout',
28
29
  doNotUse: 'Whole card → combat_card',
29
30
  preferOver: [],
30
- outcome: 'One bout in full: RAF per-period scores / BKFC per-round punch stats (Starter) / Bellator per-round official stats in UFC format (Pro: the archive is older than the Free/Starter window)',
31
+ outcome: 'One bout in full: RAF per-period scores / BKFC per-round punch stats (Starter) / MMA archive per-round official stats in UFC format (Pro: the archives are older than the Free/Starter window)',
31
32
  parallelSafe: true,
32
- games: ['raf', 'bkfc', 'bellator'],
33
+ games: [...COMBAT_LEAGUES],
33
34
  jobs: ['match_page'],
34
35
  exampleArgs: { league: 'raf', boutId: 'raf13--belal-muhammad--colby-covington' },
35
36
  },
@@ -37,9 +38,9 @@ const COMBAT_CATALOG = [
37
38
  name: 'combat_athlete',
38
39
  doNotUse: "Two athletes' meetings → combat_h2h",
39
40
  preferOver: [],
40
- outcome: 'RAF athlete / BKFC or Bellator fighter page: profile + headshot, recent bouts, career stats (Starter); slug or name; Bellator fighters share the UFC slug (ufcFighterSlug)',
41
+ outcome: 'RAF athlete / BKFC or MMA archive fighter page: profile + headshot, recent bouts, career stats (Starter); slug or name; archive fighters share the UFC slug (ufcFighterSlug); league mma = whole non-UFC career with recordByPromotion',
41
42
  parallelSafe: true,
42
- games: ['raf', 'bkfc', 'bellator'],
43
+ games: [...COMBAT_LEAGUES],
43
44
  jobs: ['player_form', 'media'],
44
45
  exampleArgs: { league: 'raf', athlete: 'Kyle Snyder', historyLimit: 5 },
45
46
  },
@@ -47,19 +48,19 @@ const COMBAT_CATALOG = [
47
48
  name: 'combat_h2h',
48
49
  doNotUse: "One athlete's whole history → combat_athlete",
49
50
  preferOver: [],
50
- outcome: 'Head-to-head between two RAF athletes, two BKFC fighters or two Bellator fighters',
51
+ outcome: 'Head-to-head between two RAF athletes, two BKFC fighters or two MMA archive fighters',
51
52
  parallelSafe: true,
52
- games: ['raf', 'bkfc', 'bellator'],
53
+ games: [...COMBAT_LEAGUES],
53
54
  jobs: ['h2h', 'preview'],
54
55
  exampleArgs: { league: 'bkfc', athleteA: 'ryan-petersen', athleteB: 'gregoris-cisneros' },
55
56
  },
56
57
  {
57
58
  name: 'combat_rankings',
58
- doNotUse: 'UFC rankings → standings / rankings tools (RAF/BKFC/Bellator never appear in UFC answers); Bellator has no rankings (NOT_IMPLEMENTED)',
59
+ doNotUse: 'UFC rankings → standings / rankings tools (RAF/BKFC/MMA archives never appear in UFC answers); the MMA archives have no rankings (NOT_IMPLEMENTED)',
59
60
  preferOver: [],
60
61
  outcome: 'RAF champions per division / BKFC official rankings and champions',
61
62
  parallelSafe: true,
62
- games: ['raf', 'bkfc', 'bellator'],
63
+ games: [...COMBAT_LEAGUES],
63
64
  jobs: ['standings'],
64
65
  exampleArgs: { league: 'bkfc', division: 'heavyweight' },
65
66
  },
@@ -552,8 +553,8 @@ const GAMES_META = [
552
553
  label: 'UFC / MMA',
553
554
  depth: 'medium',
554
555
  notes: 'Fighter slugs; boutId; live + rankings. Includes Dana White\'s Contender Series and Road to UFC (2022-2026, all 19 cards ' +
555
- 'with official round stats; event slugs like road-to-ufc-2-7, titles "Road to UFC 2.7"). Bellator is NOT UFC: use the ' +
556
- 'combat_* tools with league bellator, and never mix its fights into UFC answers.',
556
+ 'with official round stats; event slugs like road-to-ufc-2-7, titles "Road to UFC 2.7"). Bellator, PRIDE, Strikeforce, WEC, ' +
557
+ 'DREAM, EliteXC and Affliction are NOT UFC: combat_* tools with that league (or mma), never mixed into UFC answers.',
557
558
  },
558
559
  ];
559
560
  export const listCapabilities = {
@@ -797,6 +798,15 @@ const ALLOWLIST_PREFIXES = [
797
798
  '/raf',
798
799
  '/bkfc',
799
800
  '/bellator',
801
+ '/pride',
802
+ '/strikeforce',
803
+ '/wec',
804
+ '/dream',
805
+ '/elitexc',
806
+ '/affliction',
807
+ '/pfl',
808
+ '/oktagon',
809
+ '/mma',
800
810
  '/openapi.json',
801
811
  ];
802
812
  function pathAllowed(path) {
@@ -820,7 +830,7 @@ Prefer curated tools for all standard jobs (live, schedule, profiles, standings,
820
830
 
821
831
  Do not use when: a curated tool covers the outcome. Avoid parallel storms; same plan rate limits apply.
822
832
 
823
- Path must start with / and match allowlisted prefixes: /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /tennis, /raf, /bkfc, /bellator.
833
+ Path must start with / and match allowlisted prefixes: /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /tennis, /raf, /bkfc, /bellator, /pride, /strikeforce, /wec, /dream, /elitexc, /affliction, /pfl, /oktagon, /mma.
824
834
  Rejects absolute URLs and path traversal → PATH_NOT_ALLOWED.
825
835
 
826
836
  Parallel-safe: yes but discouraged in bulk. Upstream cost: 1.
@@ -881,7 +891,7 @@ Example: { "method": "GET", "path": "/cs2/rankings/teams", "queryJson": "{\\"pag
881
891
  source: 'call_api',
882
892
  requestId,
883
893
  tookMs: Date.now() - started,
884
- hint: 'Use prefixes /health /lol /cs2 /dota2 /cod /ufc /fortnite /tennis /raf /bkfc /bellator',
894
+ hint: 'Use prefixes /health /lol /cs2 /dota2 /cod /ufc /fortnite /tennis /raf /bkfc /bellator /pride /strikeforce /wec /dream /elitexc /affliction /pfl /oktagon /mma',
885
895
  });
886
896
  }
887
897
  let query = {};
@@ -982,10 +992,10 @@ Example: { "game": "ufc", "q": "rankings" }`,
982
992
  const started = Date.now();
983
993
  const requestId = newRequestId();
984
994
  const gameRaw = typeof args.game === 'string' ? args.game.toLowerCase().trim() : '';
985
- if (gameRaw && !gameRaw.match(/^(lol|cs2|dota2|cod|ufc|fortnite|tennis|raf|bkfc|bellator)$/)) {
995
+ if (gameRaw && !gameRaw.match(/^(lol|cs2|dota2|cod|ufc|fortnite|tennis|raf|bkfc|bellator|pride|strikeforce|wec|dream|elitexc|affliction|pfl|oktagon|mma)$/)) {
986
996
  return errorEnvelope({
987
997
  code: 'UNSUPPORTED_GAME',
988
- message: `unsupported game "${gameRaw}"; use lol|cs2|dota2|cod|ufc|fortnite|tennis|raf|bkfc|bellator`,
998
+ message: `unsupported game "${gameRaw}"; use lol|cs2|dota2|cod|ufc|fortnite|tennis|raf|bkfc|bellator|pride|strikeforce|wec|dream|elitexc|affliction|pfl|oktagon|mma`,
989
999
  game: null,
990
1000
  source: 'list_routes',
991
1001
  requestId,
@@ -1008,7 +1018,7 @@ Example: { "game": "ufc", "q": "rankings" }`,
1008
1018
  httpStatus: res.status,
1009
1019
  recover: [
1010
1020
  'Call list_capabilities for curated tools that need no route knowledge',
1011
- 'call_api accepts /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /tennis, /raf, /bkfc, /bellator',
1021
+ 'call_api accepts /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /tennis, /raf, /bkfc, /bellator, /pride, /strikeforce, /wec, /dream, /elitexc, /affliction, /pfl, /oktagon, /mma',
1012
1022
  ],
1013
1023
  });
1014
1024
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cito-mcp",
3
- "version": "0.4.9",
3
+ "version": "0.5.1",
4
4
  "description": "Standalone MCP server for the Cito esports and sports API \u2014 curated outcome tools for agents (live scoreboards, round economy, opening duels, clutches, vetoes, rosters, tennis, mma).",
5
5
  "type": "module",
6
6
  "homepage": "https://cito.gg",