cito-mcp 0.5.4 → 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 +7 -0
- package/dist/tools/cs2.js +40 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -550,6 +550,13 @@ 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
|
+
|
|
553
560
|
### 0.5.4
|
|
554
561
|
|
|
555
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`.
|
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/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",
|