cito-mcp 0.5.1 → 0.5.2
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/tools/cs2.js +74 -0
- package/dist/tools/meta.js +10 -0
- package/package.json +3 -3
package/dist/tools/cs2.js
CHANGED
|
@@ -362,6 +362,79 @@ export const cs2RosterTransfers = {
|
|
|
362
362
|
});
|
|
363
363
|
},
|
|
364
364
|
};
|
|
365
|
+
const POSITION_VIEWS = ['grenades', 'positions', 'heatmap'];
|
|
366
|
+
const POSITION_FILTERS = ['type', 'side', 'round', 'player', 'team', 'kind', 'grid', 'map', 'days', 'limit'];
|
|
367
|
+
export const cs2Positions = {
|
|
368
|
+
name: 'cs2_positions',
|
|
369
|
+
description: 'CS2 positional data from parsed demos. With mapId: view=grenades (every grenade with landing spot, thrower, round, duration), '
|
|
370
|
+
+ 'view=positions (grenades, bomb plants/defuses and kills with attacker/victim spots in one timeline) or view=heatmap (counts per radar cell; '
|
|
371
|
+
+ 'kind=utility|smoke|flash|he|molotov|decoy|bomb_plant|bomb_defuse|kill|death). With playerId instead: that player\'s grenades across recent maps '
|
|
372
|
+
+ '(pass map for a heatmap). Every point has game coordinates and a 0..1 radar point. Read the response notes and null fields for what is not recorded.',
|
|
373
|
+
inputSchema: {
|
|
374
|
+
type: 'object',
|
|
375
|
+
properties: {
|
|
376
|
+
mapId: stringSchema('Map/game id from match maps, e.g. "cs2-match-2399437-map-1".', 'cs2-match-2399437-map-1'),
|
|
377
|
+
playerId: stringSchema('Player id or slug (e.g. "ropz"). Use instead of mapId for one player across maps.', 'ropz'),
|
|
378
|
+
view: { type: 'string', enum: [...POSITION_VIEWS], description: 'For mapId: grenades (default), positions or heatmap.' },
|
|
379
|
+
type: stringSchema('Grenade type: smoke, flash, he, molotov or decoy.'),
|
|
380
|
+
side: stringSchema('T or CT.'),
|
|
381
|
+
round: { type: 'integer', minimum: 1, description: 'Only this round.' },
|
|
382
|
+
player: stringSchema('With mapId: only this player (id or nickname).'),
|
|
383
|
+
team: stringSchema('With mapId + view=grenades: only this team (id or name).'),
|
|
384
|
+
kind: stringSchema('view=heatmap: utility, smoke, flash, he, molotov, decoy, bomb_plant, bomb_defuse, kill or death. view=positions: grenade,bomb,kill.'),
|
|
385
|
+
grid: { type: 'integer', minimum: 4, maximum: 128, description: 'Heatmap cells per side (default 32).' },
|
|
386
|
+
map: stringSchema('With playerId: map name (Mirage, Inferno...) to get a heatmap.'),
|
|
387
|
+
days: { type: 'integer', minimum: 1, maximum: 3650, description: 'With playerId: look-back window in days (default 180).' },
|
|
388
|
+
limit: limitSchema({ default: 200, max: 5000 }),
|
|
389
|
+
},
|
|
390
|
+
additionalProperties: false,
|
|
391
|
+
},
|
|
392
|
+
annotations: READ_TOOL_ANNOTATIONS,
|
|
393
|
+
handler: async (args, ctx) => {
|
|
394
|
+
const reqId = newRequestId();
|
|
395
|
+
const mapId = typeof args.mapId === 'string' ? args.mapId.trim() : '';
|
|
396
|
+
const playerId = typeof args.playerId === 'string' ? args.playerId.trim() : '';
|
|
397
|
+
if (!mapId && !playerId) {
|
|
398
|
+
return errorEnvelope({
|
|
399
|
+
code: 'VALIDATION',
|
|
400
|
+
message: 'mapId or playerId is required',
|
|
401
|
+
game: 'cs2',
|
|
402
|
+
source: 'cs2_positions',
|
|
403
|
+
requestId: reqId,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const view = POSITION_VIEWS.includes(args.view) ? String(args.view) : 'grenades';
|
|
407
|
+
const path = playerId
|
|
408
|
+
? `/cs2/players/${encodeURIComponent(playerId)}/utility`
|
|
409
|
+
: `/cs2/maps/${encodeURIComponent(mapId)}/${view === 'grenades' ? 'utility' : view}`;
|
|
410
|
+
const query = {};
|
|
411
|
+
for (const key of POSITION_FILTERS) {
|
|
412
|
+
const value = args[key];
|
|
413
|
+
if (typeof value === 'string' && value.trim())
|
|
414
|
+
query[key] = value.trim();
|
|
415
|
+
else if (typeof value === 'number' && Number.isFinite(value))
|
|
416
|
+
query[key] = value;
|
|
417
|
+
}
|
|
418
|
+
const res = await fetchJson(ctx, path, { query });
|
|
419
|
+
if (!res.ok) {
|
|
420
|
+
return errorEnvelope({
|
|
421
|
+
code: mapHttpToCode(res.status),
|
|
422
|
+
message: `CS2 positions fetch failed: ${res.status} on ${path}`,
|
|
423
|
+
game: 'cs2',
|
|
424
|
+
source: 'cs2_positions',
|
|
425
|
+
requestId: reqId,
|
|
426
|
+
httpStatus: res.status,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return successEnvelope({
|
|
430
|
+
data: unwrapPayload(res.data),
|
|
431
|
+
game: 'cs2',
|
|
432
|
+
source: 'cs2_positions',
|
|
433
|
+
requestId: reqId,
|
|
434
|
+
rateLimit: res.headers,
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
};
|
|
365
438
|
export const cs2Tools = [
|
|
366
439
|
cs2LiveScoreboard,
|
|
367
440
|
cs2RoundEconomy,
|
|
@@ -371,4 +444,5 @@ export const cs2Tools = [
|
|
|
371
444
|
cs2UtilityLeaderboard,
|
|
372
445
|
cs2VetoSequence,
|
|
373
446
|
cs2RosterTransfers,
|
|
447
|
+
cs2Positions,
|
|
374
448
|
];
|
package/dist/tools/meta.js
CHANGED
|
@@ -442,6 +442,16 @@ const TOOL_CATALOG = [
|
|
|
442
442
|
preferOver: ['unindexed round event aggregation'],
|
|
443
443
|
doNotUse: 'For standard weapon ratings',
|
|
444
444
|
},
|
|
445
|
+
{
|
|
446
|
+
name: 'cs2_positions',
|
|
447
|
+
outcome: 'CS2 grenade landing spots, bomb plants, kill/death spots and radar heatmaps for a map or a player',
|
|
448
|
+
parallelSafe: true,
|
|
449
|
+
games: ['cs2'],
|
|
450
|
+
jobs: ['match_page', 'player_form'],
|
|
451
|
+
exampleArgs: { mapId: 'cs2-match-2399437-map-1', view: 'heatmap', kind: 'smoke' },
|
|
452
|
+
preferOver: ['call_api /cs2/maps/{mapId}/utility|positions|heatmap', 'parsing /cs2/games/{id}/rounds events for coordinates'],
|
|
453
|
+
doNotUse: 'For live matches (positions come from the demo, after the map ends)',
|
|
454
|
+
},
|
|
445
455
|
{
|
|
446
456
|
name: 'cs2_veto_sequence',
|
|
447
457
|
outcome: 'Map pick/ban sequence and veto history for a CS2 match',
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cito-mcp",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Standalone MCP server for the Cito esports and sports API
|
|
3
|
+
"version": "0.5.2",
|
|
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",
|
|
7
|
-
"//REPOSITORY": "Intentionally NOT set. cito-api-scraper and cito.gg are PRIVATE repos and must stay that way, so a repository URL pointing at them would render as a broken 404 link on the public npm page and would not satisfy MCP Registry npm ownership validation. Set this only if a PUBLIC repo for this package is ever created (e.g. achillesscriptsvip/cito-mcp with this directory as root)
|
|
7
|
+
"//REPOSITORY": "Intentionally NOT set. cito-api-scraper and cito.gg are PRIVATE repos and must stay that way, so a repository URL pointing at them would render as a broken 404 link on the public npm page and would not satisfy MCP Registry npm ownership validation. Set this only if a PUBLIC repo for this package is ever created (e.g. achillesscriptsvip/cito-mcp with this directory as root) — then use `git+https://github.com/OWNER/REPO.git` and mirror it in the `mcpName`/server.json `name` pair. Do not point it at the private monorepo.",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"mcp",
|
|
10
10
|
"model-context-protocol",
|