cito-mcp 0.5.3 → 0.5.5
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 +12 -0
- package/dist/envelope.js +1 -1
- package/dist/instructions.js +1 -0
- package/dist/tools/cs2.js +40 -15
- package/dist/tools/meta.js +41 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -550,6 +550,18 @@ claude mcp add cito -e CITO_API_KEY=cito_… "--" npx -y cito-mcp
|
|
|
550
550
|
|
|
551
551
|
## Changelog (summary)
|
|
552
552
|
|
|
553
|
+
### 0.5.5
|
|
554
|
+
|
|
555
|
+
- **`cs2_veto_sequence` returns the veto.** It called the match head-to-head route and answered with the two teams' head-to-head record (`cs2_team_h2h`) instead of the veto (0.5.1 to 0.5.4). It now calls `/cs2/matches/{matchId}/veto`: `vetoOrder` (every ban, pick and the decider, in order), `vetoStatus` (`complete`, `pending`, `forfeit_no_veto`, `unavailable`), `bestOf` and the maps.
|
|
556
|
+
- **`cs2_opening_duels` with `playerId` works.** It called a route the API does not have (every call was a 404). A player's opening duels now come from the career totals: `openingKills` / `openingDeaths` (null when fewer than 90% of the player's maps record them), the recorded sums, coverage and success rate.
|
|
557
|
+
- **`cs2_team_map_stats` takes `opponentId`** for a map-by-map comparison of two teams (`/cs2/teams/{team}/map-pool/vs/{opponent}`: win rates, round difference, pick/ban/decider rates, their meetings per map, edge). Map rows carry `inPool` / `poolStatus`.
|
|
558
|
+
- `cs2_round_economy` points to `/cs2/maps/{mapId}/economy` for per-player money each round.
|
|
559
|
+
|
|
560
|
+
### 0.5.4
|
|
561
|
+
|
|
562
|
+
- **Apex Legends through `call_api`.** `/apex/...` paths (ALGS events, standings, scores) were refused with `PATH_NOT_ALLOWED` although they are a live REST product. They are allowed now and listed in `list_capabilities`.
|
|
563
|
+
- **`api_health` reports your real plan and limits**: the tier plus the per-minute and monthly limit, used and remaining, read from the API's `/health`, instead of `null`.
|
|
564
|
+
|
|
553
565
|
### 0.5.3
|
|
554
566
|
|
|
555
567
|
- **Paces itself and retries rate limits.** One question fans out into several API calls, and on the Free plan (10 requests/minute on average, bursts to 30) those bursts came back as `429`s the MCP never waited out. Requests are now capped at 4 in flight per API key (2 for a minute after a `429`), and once the API sends `X-RateLimit-Remaining-Minute: 0` the next request waits for the minute window instead of being refused. Paid keys never receive those headers, so they never wait.
|
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, MMA archives /bellator /pride /strikeforce /wec /dream /elitexc /affliction /pfl /oktagon /mma',
|
|
41
|
+
'Use only allowlisted path prefixes: /health /lol /cs2 /dota2 /cod /ufc /fortnite /apex /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: [
|
package/dist/instructions.js
CHANGED
|
@@ -17,6 +17,7 @@ game enum (unless a tool documents otherwise): lol | cs2 | dota2 | ufc | cod | t
|
|
|
17
17
|
- Default when the user named one title: that game. Default for "what's live?" / multi-title: omit game or all on tools that accept it.
|
|
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
|
+
- Apex Legends (ALGS) is call_api only: /apex/algs/events, /apex/algs/events/{seriesId}/scores, /apex/algs/players, /apex/algs/global-stats/{eventId}. Not on the game enum and not in the OpenAPI spec; list_routes { game: "apex" } returns the path list as a note.
|
|
20
21
|
- 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
22
|
- 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
23
|
- 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.
|
package/dist/tools/cs2.js
CHANGED
|
@@ -59,7 +59,8 @@ export const cs2LiveScoreboard = {
|
|
|
59
59
|
};
|
|
60
60
|
export const cs2RoundEconomy = {
|
|
61
61
|
name: 'cs2_round_economy',
|
|
62
|
-
description: 'Round-by-round tactical economy for a CS2 map. Classifies every round buy type (Full Buy, Force Buy, Semi-Eco, Full Eco), team equipment spend, freeze-time equipment values, and loss bonus counter.'
|
|
62
|
+
description: 'Round-by-round tactical economy for a CS2 map. Classifies every round buy type (Full Buy, Force Buy, Semi-Eco, Full Eco), team equipment spend, freeze-time equipment values, and loss bonus counter. '
|
|
63
|
+
+ 'For money per PLAYER each round (startMoney, moneySpent, moneyLeft, equipmentValue, buyType, purchases) use call_api GET /cs2/maps/{mapId}/economy (by=player|team|round; paid plans).',
|
|
63
64
|
inputSchema: {
|
|
64
65
|
type: 'object',
|
|
65
66
|
properties: {
|
|
@@ -123,9 +124,9 @@ export const cs2OpeningDuels = {
|
|
|
123
124
|
const rawPlayer = typeof args.playerId === 'string' ? args.playerId.trim() : '';
|
|
124
125
|
const limit = clampInt(args.limit, 20, 1, 100);
|
|
125
126
|
const path = rawPlayer
|
|
126
|
-
? `/cs2/players/${encodeURIComponent(rawPlayer)}/
|
|
127
|
+
? `/cs2/players/${encodeURIComponent(rawPlayer)}/career`
|
|
127
128
|
: `/cs2/stats/opening-duels`;
|
|
128
|
-
const res = await fetchJson(ctx, path, { query: { limit } });
|
|
129
|
+
const res = await fetchJson(ctx, path, rawPlayer ? {} : { query: { limit } });
|
|
129
130
|
if (!res.ok) {
|
|
130
131
|
return errorEnvelope({
|
|
131
132
|
code: mapHttpToCode(res.status),
|
|
@@ -137,8 +138,29 @@ export const cs2OpeningDuels = {
|
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
const payload = unwrap(res.data);
|
|
141
|
+
let data = payload;
|
|
142
|
+
if (rawPlayer) {
|
|
143
|
+
const p = asRecord(payload) ?? {};
|
|
144
|
+
const t = asRecord(p.totals) ?? {};
|
|
145
|
+
const fk = typeof t.openingKillsRecorded === 'number' ? t.openingKillsRecorded : null;
|
|
146
|
+
const fd = typeof t.openingDeathsRecorded === 'number' ? t.openingDeathsRecorded : null;
|
|
147
|
+
data = {
|
|
148
|
+
entityType: 'cs2_player_opening_duels',
|
|
149
|
+
playerId: p.playerId ?? null,
|
|
150
|
+
playerNickname: p.playerNickname ?? null,
|
|
151
|
+
maps: t.maps ?? null,
|
|
152
|
+
openingKills: t.openingKills ?? null,
|
|
153
|
+
openingDeaths: t.openingDeaths ?? null,
|
|
154
|
+
openingKillsRecorded: fk,
|
|
155
|
+
openingDeathsRecorded: fd,
|
|
156
|
+
openingDuelMaps: t.openingDuelMaps ?? null,
|
|
157
|
+
openingDuelCoverage: t.openingDuelCoverage ?? null,
|
|
158
|
+
openingDuelSuccessRate: fk !== null && fd !== null && fk + fd > 0 ? Math.round((fk / (fk + fd)) * 10000) / 10000 : null,
|
|
159
|
+
source: `/cs2/players/${rawPlayer}/career`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
140
162
|
return successEnvelope({
|
|
141
|
-
data
|
|
163
|
+
data,
|
|
142
164
|
game: 'cs2',
|
|
143
165
|
source: 'cs2_opening_duels',
|
|
144
166
|
requestId: reqId,
|
|
@@ -188,16 +210,18 @@ export const cs2Clutches = {
|
|
|
188
210
|
};
|
|
189
211
|
export const cs2TeamMapStats = {
|
|
190
212
|
name: 'cs2_team_map_stats',
|
|
191
|
-
description: 'Team win rates, round win rates, and CT/T side win splits
|
|
213
|
+
description: 'Team win rates, round win rates, and CT/T side win splits per map over the last N days. Each map carries inPool / poolStatus (current Active Duty pool) and removedAt. '
|
|
214
|
+
+ 'Pass opponentId to compare two teams map by map instead (GET /cs2/teams/{team}/map-pool/vs/{opponent}): win rates, round difference, pick/ban/decider rates, their meetings on each map and an edge per map.',
|
|
192
215
|
inputSchema: {
|
|
193
216
|
type: 'object',
|
|
194
217
|
properties: {
|
|
195
218
|
teamId: stringSchema('Team ID or slug (e.g. "cs2-team-4608" or "natus-vincere"). Required.', 'cs2-team-4608'),
|
|
196
219
|
days: {
|
|
197
220
|
type: 'integer',
|
|
198
|
-
description: 'Timeframe in days: 30, 90, 180, or 365 (default 90).',
|
|
221
|
+
description: 'Timeframe in days: 30, 90, 180, or 365 (default 90; with opponentId the API default is 180).',
|
|
199
222
|
default: 90,
|
|
200
223
|
},
|
|
224
|
+
opponentId: stringSchema('Optional opponent team ID or slug (e.g. "mouz"): returns the map-pool head-to-head of the two teams.', 'mouz'),
|
|
201
225
|
},
|
|
202
226
|
required: ['teamId'],
|
|
203
227
|
additionalProperties: false,
|
|
@@ -215,9 +239,12 @@ export const cs2TeamMapStats = {
|
|
|
215
239
|
requestId: reqId,
|
|
216
240
|
});
|
|
217
241
|
}
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
const
|
|
242
|
+
const rawOpponent = typeof args.opponentId === 'string' ? args.opponentId.trim() : '';
|
|
243
|
+
const days = typeof args.days === 'number' ? args.days : rawOpponent ? undefined : 90;
|
|
244
|
+
const path = rawOpponent
|
|
245
|
+
? `/cs2/teams/${encodeURIComponent(rawTeam)}/map-pool/vs/${encodeURIComponent(rawOpponent)}`
|
|
246
|
+
: `/cs2/teams/${encodeURIComponent(rawTeam)}/map-stats`;
|
|
247
|
+
const res = await fetchJson(ctx, path, days === undefined ? {} : { query: { days } });
|
|
221
248
|
if (!res.ok) {
|
|
222
249
|
return errorEnvelope({
|
|
223
250
|
code: mapHttpToCode(res.status),
|
|
@@ -276,7 +303,8 @@ export const cs2UtilityLeaderboard = {
|
|
|
276
303
|
};
|
|
277
304
|
export const cs2VetoSequence = {
|
|
278
305
|
name: 'cs2_veto_sequence',
|
|
279
|
-
description: 'Map pick/ban veto sequence for a CS2 match
|
|
306
|
+
description: 'Map pick/ban veto sequence for a CS2 match (GET /cs2/matches/{matchId}/veto). Returns vetoOrder (each ban, pick and the decider in order, with the team), '
|
|
307
|
+
+ 'vetoStatus (complete | pending | forfeit_no_veto | unavailable; vetoOrder is [] unless complete), bestOf and the maps.',
|
|
280
308
|
inputSchema: {
|
|
281
309
|
type: 'object',
|
|
282
310
|
properties: {
|
|
@@ -298,8 +326,7 @@ export const cs2VetoSequence = {
|
|
|
298
326
|
requestId: reqId,
|
|
299
327
|
});
|
|
300
328
|
}
|
|
301
|
-
const
|
|
302
|
-
const path = `/cs2/matches/${encodeURIComponent(cleanId)}/head-to-head`;
|
|
329
|
+
const path = `/cs2/matches/${encodeURIComponent(rawId)}/veto`;
|
|
303
330
|
const res = await fetchJson(ctx, path);
|
|
304
331
|
if (!res.ok) {
|
|
305
332
|
return errorEnvelope({
|
|
@@ -312,10 +339,8 @@ export const cs2VetoSequence = {
|
|
|
312
339
|
});
|
|
313
340
|
}
|
|
314
341
|
const payload = unwrap(res.data);
|
|
315
|
-
const r = asRecord(payload);
|
|
316
|
-
const veto = r?.veto ?? r?.vetoSequence ?? payload;
|
|
317
342
|
return successEnvelope({
|
|
318
|
-
data:
|
|
343
|
+
data: payload,
|
|
319
344
|
game: 'cs2',
|
|
320
345
|
source: 'cs2_veto_sequence',
|
|
321
346
|
requestId: reqId,
|
package/dist/tools/meta.js
CHANGED
|
@@ -476,7 +476,7 @@ const TOOL_CATALOG = [
|
|
|
476
476
|
name: 'list_routes',
|
|
477
477
|
outcome: 'Index of raw REST routes from the live OpenAPI spec (method, path, summary)',
|
|
478
478
|
parallelSafe: true,
|
|
479
|
-
games: [...PRIMARY_GAMES, 'fortnite'],
|
|
479
|
+
games: [...PRIMARY_GAMES, 'fortnite', 'apex'],
|
|
480
480
|
jobs: ['app_scaffold'],
|
|
481
481
|
exampleArgs: { game: 'ufc', q: 'rankings' },
|
|
482
482
|
preferOver: ['guessing a REST path for call_api', 'fetching the full 90KB spec by hand'],
|
|
@@ -486,7 +486,7 @@ const TOOL_CATALOG = [
|
|
|
486
486
|
name: 'call_api',
|
|
487
487
|
outcome: 'Allowlisted raw REST escape hatch (unshaped data.raw)',
|
|
488
488
|
parallelSafe: true,
|
|
489
|
-
games: [...PRIMARY_GAMES, 'fortnite'],
|
|
489
|
+
games: [...PRIMARY_GAMES, 'fortnite', 'apex'],
|
|
490
490
|
jobs: ['app_scaffold'],
|
|
491
491
|
exampleArgs: { method: 'GET', path: '/cs2/rankings/teams', queryJson: '{"limit":20}' },
|
|
492
492
|
preferOver: [],
|
|
@@ -558,6 +558,14 @@ const GAMES_META = [
|
|
|
558
558
|
{ id: 'cs2', label: 'Counter-Strike 2', depth: 'deep', notes: 'Prefixed ids; live path /cs2/live; trends/rankings' },
|
|
559
559
|
{ id: 'dota2', label: 'Dota 2', depth: 'thin', notes: 'Numeric ids; radar; no standings/roster endpoints' },
|
|
560
560
|
{ id: 'cod', label: 'Call of Duty (CDL)', depth: 'medium', notes: 'Org slugs; CDL standings; UUID match ids' },
|
|
561
|
+
{
|
|
562
|
+
id: 'apex',
|
|
563
|
+
label: 'Apex Legends (ALGS)',
|
|
564
|
+
depth: 'thin',
|
|
565
|
+
notes: 'call_api only (no curated tools): /apex/algs/events, /apex/algs/events/{seriesId} (+ /scores, /bans), ' +
|
|
566
|
+
'/apex/algs/matches, /apex/algs/players (+ /search), /apex/algs/global-stats/{eventId} (+ /players, /legends), ' +
|
|
567
|
+
'/apex/algs/transactions, /apex/algs/regions, /apex/algs/maps. Not in the OpenAPI spec yet (list_routes { game: "apex" } returns this path list as a note).',
|
|
568
|
+
},
|
|
561
569
|
{
|
|
562
570
|
id: 'ufc',
|
|
563
571
|
label: 'UFC / MMA',
|
|
@@ -776,20 +784,38 @@ Example: { "includeGameProbes": true }`,
|
|
|
776
784
|
else if (keyValid) {
|
|
777
785
|
includedGames.push(...PRIMARY_GAMES);
|
|
778
786
|
}
|
|
787
|
+
const healthBody = health.ok && health.data && typeof health.data === 'object' && !Array.isArray(health.data)
|
|
788
|
+
? health.data
|
|
789
|
+
: null;
|
|
790
|
+
const limits = healthBody?.rateLimit && typeof healthBody.rateLimit === 'object'
|
|
791
|
+
? healthBody.rateLimit
|
|
792
|
+
: null;
|
|
793
|
+
const perMinute = limits?.perMinute && typeof limits.perMinute === 'object'
|
|
794
|
+
? limits.perMinute
|
|
795
|
+
: null;
|
|
796
|
+
const headerRate = rateLimit;
|
|
797
|
+
const tier = headerRate.tier ?? (typeof healthBody?.tier === 'string' ? healthBody.tier : null);
|
|
798
|
+
const mergedRateLimit = {
|
|
799
|
+
...headerRate,
|
|
800
|
+
tier,
|
|
801
|
+
limit: headerRate.limit ?? (typeof perMinute?.limit === 'number' ? perMinute.limit : null),
|
|
802
|
+
};
|
|
779
803
|
return successEnvelope({
|
|
780
804
|
source: 'api_health',
|
|
781
805
|
game: null,
|
|
782
806
|
requestId,
|
|
783
807
|
tookMs: Date.now() - started,
|
|
784
808
|
upstreamCalls,
|
|
785
|
-
rateLimit,
|
|
809
|
+
rateLimit: mergedRateLimit,
|
|
786
810
|
data: {
|
|
787
811
|
reachable,
|
|
788
812
|
keyValid,
|
|
789
813
|
answeredBy,
|
|
790
814
|
httpStatus: authedStatus,
|
|
791
|
-
tier
|
|
792
|
-
|
|
815
|
+
tier,
|
|
816
|
+
plan: typeof healthBody?.plan === 'string' ? healthBody.plan : null,
|
|
817
|
+
rateLimit: mergedRateLimit,
|
|
818
|
+
...(limits ? { limits } : {}),
|
|
793
819
|
includedGames,
|
|
794
820
|
...(probes.length ? { probes } : {}),
|
|
795
821
|
},
|
|
@@ -804,6 +830,7 @@ const ALLOWLIST_PREFIXES = [
|
|
|
804
830
|
'/cod',
|
|
805
831
|
'/ufc',
|
|
806
832
|
'/fortnite',
|
|
833
|
+
'/apex',
|
|
807
834
|
'/tennis',
|
|
808
835
|
'/raf',
|
|
809
836
|
'/bkfc',
|
|
@@ -840,7 +867,7 @@ Prefer curated tools for all standard jobs (live, schedule, profiles, standings,
|
|
|
840
867
|
|
|
841
868
|
Do not use when: a curated tool covers the outcome. Avoid parallel storms; same plan rate limits apply.
|
|
842
869
|
|
|
843
|
-
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.
|
|
870
|
+
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.
|
|
844
871
|
Rejects absolute URLs and path traversal → PATH_NOT_ALLOWED.
|
|
845
872
|
|
|
846
873
|
Parallel-safe: yes but discouraged in bulk. Upstream cost: 1.
|
|
@@ -901,7 +928,7 @@ Example: { "method": "GET", "path": "/cs2/rankings/teams", "queryJson": "{\\"pag
|
|
|
901
928
|
source: 'call_api',
|
|
902
929
|
requestId,
|
|
903
930
|
tookMs: Date.now() - started,
|
|
904
|
-
hint: 'Use prefixes /health /lol /cs2 /dota2 /cod /ufc /fortnite /tennis /raf /bkfc /bellator /pride /strikeforce /wec /dream /elitexc /affliction /pfl /oktagon /mma',
|
|
931
|
+
hint: 'Use prefixes /health /lol /cs2 /dota2 /cod /ufc /fortnite /apex /tennis /raf /bkfc /bellator /pride /strikeforce /wec /dream /elitexc /affliction /pfl /oktagon /mma',
|
|
905
932
|
});
|
|
906
933
|
}
|
|
907
934
|
let query = {};
|
|
@@ -962,6 +989,10 @@ Example: { "method": "GET", "path": "/cs2/rankings/teams", "queryJson": "{\\"pag
|
|
|
962
989
|
const SPEC_OMITS = {
|
|
963
990
|
tennis: 'The published OpenAPI spec documents no /tennis paths, but tennis routes exist and work (players, matches, h2h, rankings, tournaments, live). Use the curated tools with game tennis, or call_api with /tennis/* paths.',
|
|
964
991
|
lol: 'The published OpenAPI spec documents no /lol paths, but LoL routes exist and work. Use the curated LoL tools (live_matches, upcoming_schedule, team_profile, standings, player_profile); for raw access, /lol/* is allowlisted for call_api even though it is undocumented.',
|
|
992
|
+
apex: 'The published OpenAPI spec documents no /apex paths, but the Apex Legends ALGS routes exist and work through call_api: '
|
|
993
|
+
+ 'GET /apex/algs/events, /apex/algs/events/{seriesId} (+ /scores, /bans), /apex/algs/matches, /apex/algs/players (+ /search?q=), '
|
|
994
|
+
+ '/apex/algs/global-stats (+ /{eventId}, /{eventId}/players, /{eventId}/legends), /apex/algs/transactions, /apex/algs/tournaments, '
|
|
995
|
+
+ '/apex/algs/regions, /apex/algs/maps.',
|
|
965
996
|
};
|
|
966
997
|
export const listRoutes = {
|
|
967
998
|
name: 'list_routes',
|
|
@@ -1002,10 +1033,10 @@ Example: { "game": "ufc", "q": "rankings" }`,
|
|
|
1002
1033
|
const started = Date.now();
|
|
1003
1034
|
const requestId = newRequestId();
|
|
1004
1035
|
const gameRaw = typeof args.game === 'string' ? args.game.toLowerCase().trim() : '';
|
|
1005
|
-
if (gameRaw && !gameRaw.match(/^(lol|cs2|dota2|cod|ufc|fortnite|tennis|raf|bkfc|bellator|pride|strikeforce|wec|dream|elitexc|affliction|pfl|oktagon|mma)$/)) {
|
|
1036
|
+
if (gameRaw && !gameRaw.match(/^(lol|cs2|dota2|cod|ufc|fortnite|apex|tennis|raf|bkfc|bellator|pride|strikeforce|wec|dream|elitexc|affliction|pfl|oktagon|mma)$/)) {
|
|
1006
1037
|
return errorEnvelope({
|
|
1007
1038
|
code: 'UNSUPPORTED_GAME',
|
|
1008
|
-
message: `unsupported game "${gameRaw}"; use lol|cs2|dota2|cod|ufc|fortnite|tennis|raf|bkfc|bellator|pride|strikeforce|wec|dream|elitexc|affliction|pfl|oktagon|mma`,
|
|
1039
|
+
message: `unsupported game "${gameRaw}"; use lol|cs2|dota2|cod|ufc|fortnite|apex|tennis|raf|bkfc|bellator|pride|strikeforce|wec|dream|elitexc|affliction|pfl|oktagon|mma`,
|
|
1009
1040
|
game: null,
|
|
1010
1041
|
source: 'list_routes',
|
|
1011
1042
|
requestId,
|
|
@@ -1028,7 +1059,7 @@ Example: { "game": "ufc", "q": "rankings" }`,
|
|
|
1028
1059
|
httpStatus: res.status,
|
|
1029
1060
|
recover: [
|
|
1030
1061
|
'Call list_capabilities for curated tools that need no route knowledge',
|
|
1031
|
-
'call_api accepts /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /tennis, /raf, /bkfc, /bellator, /pride, /strikeforce, /wec, /dream, /elitexc, /affliction, /pfl, /oktagon, /mma',
|
|
1062
|
+
'call_api accepts /health, /lol, /cs2, /dota2, /cod, /ufc, /fortnite, /apex, /tennis, /raf, /bkfc, /bellator, /pride, /strikeforce, /wec, /dream, /elitexc, /affliction, /pfl, /oktagon, /mma',
|
|
1032
1063
|
],
|
|
1033
1064
|
});
|
|
1034
1065
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cito-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "Standalone MCP server for the Cito esports and sports API — 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",
|