@semanticintent/semantic-chirp-intelligence-mcp 4.0.1 → 4.0.3

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.
@@ -20,5 +20,5 @@
20
20
  "position": "D"
21
21
  }
22
22
  ],
23
- "updated_at": "2026-09-01T20:20:55.207Z"
23
+ "updated_at": "2026-09-01T21:24:45.509Z"
24
24
  }
@@ -30,5 +30,5 @@
30
30
  "slot": "LW"
31
31
  }
32
32
  ],
33
- "updated_at": "2026-09-01T20:20:55.063Z"
33
+ "updated_at": "2026-09-01T21:24:45.369Z"
34
34
  }
@@ -13,5 +13,5 @@
13
13
  "points": 138
14
14
  }
15
15
  ],
16
- "updated_at": "2026-09-01T20:20:55.360Z"
16
+ "updated_at": "2026-09-01T21:24:45.662Z"
17
17
  }
@@ -81,7 +81,7 @@ export class DraftPickAnalysis extends AnalysisTemplate {
81
81
  draftResultsAvailable: false,
82
82
  manualDraftedCount: draftedNames.size,
83
83
  rosterPositions,
84
- playoffWindow: this.resolvePlayoffWindow(null),
84
+ playoffWindow: this.resolvePlayoffWindow(args),
85
85
  poolCaveat: rawData.pool_caveat
86
86
  };
87
87
  }
@@ -127,26 +127,38 @@ export class DraftPickAnalysis extends AnalysisTemplate {
127
127
  const contract = this.mergeContractWithDefaults(semanticContract);
128
128
  const enhanced = ChirpIntelligence.enhance(this.toolName, analysisResults, contract);
129
129
  const top = analysisResults.candidates[0];
130
+ // The playoff clause is only truthful when a window was actually resolved.
131
+ const windowResolved = analysisResults.playoff_window?.resolved === true;
132
+ const scheduleClause = top && windowResolved && top.playoff_games !== null
133
+ ? `, and ${top.team} plays ${top.playoff_games} games in your playoff window`
134
+ : '';
130
135
  let chirp;
131
136
  if (!top) {
132
137
  chirp = 'Nobody left worth chirping about. Either the pool is empty or everyone is drafted.';
133
138
  }
134
139
  else if (top.adp_delta !== null && top.adp_delta >= 12) {
140
+ // v4 has no market ADP. average_pick is this player's rank by production,
141
+ // so the claim is "better player than this slot", not "the room drafts
142
+ // him earlier" — nothing here knows what a room does.
135
143
  chirp =
136
- `${top.name} is still sitting there at ${analysisResults.pick_number} and the room ` +
137
- `usually takes him at ${top.average_pick}. That is ${Math.round(top.adp_delta)} picks of ` +
138
- `free value. Take him before someone wakes up.`;
144
+ `${top.name} is the ${this.ordinal(top.average_pick ?? 0)} best producer left and you are ` +
145
+ `picking at ${analysisResults.pick_number}. That is ${Math.round(top.adp_delta)} slots of ` +
146
+ `talent above where you are sitting${scheduleClause}.`;
139
147
  }
140
148
  else if (top.fills_need) {
141
149
  chirp =
142
- `${top.name} fills the hole you actually have (${top.position}), and ` +
143
- `${top.team} plays ${top.playoff_games ?? '?'} games in your playoff window. ` +
150
+ `${top.name} fills the hole you actually have (${top.position})${scheduleClause}. ` +
144
151
  `Best available is a luxury; a full lineup is not.`;
145
152
  }
146
153
  else {
147
154
  chirp =
148
- `${top.name} is the pick. No bargain, no drama — just the best board-and-schedule ` +
149
- `combination left at ${analysisResults.pick_number}.`;
155
+ `${top.name} is the pick. No bargain, no drama — just the best producer left ` +
156
+ `at ${analysisResults.pick_number}${scheduleClause}.`;
157
+ }
158
+ if (!windowResolved) {
159
+ chirp +=
160
+ ' I have not scored your playoff weeks — pass playoff_start_week and ' +
161
+ 'playoff_end_week and the schedule becomes a tiebreaker.';
150
162
  }
151
163
  if (analysisResults.players_off_board === 0) {
152
164
  chirp +=
@@ -249,6 +261,18 @@ export class DraftPickAnalysis extends AnalysisTemplate {
249
261
  reasoning: this.reasoningFor(player, adpDelta, playoffGames, weekCount, fillsNeed)
250
262
  };
251
263
  }
264
+ /** 1 -> "1st", 2 -> "2nd", 23 -> "23rd". */
265
+ ordinal(n) {
266
+ const rem100 = n % 100;
267
+ if (rem100 >= 11 && rem100 <= 13)
268
+ return `${n}th`;
269
+ switch (n % 10) {
270
+ case 1: return `${n}st`;
271
+ case 2: return `${n}nd`;
272
+ case 3: return `${n}rd`;
273
+ default: return `${n}th`;
274
+ }
275
+ }
252
276
  verdictFor(adpDelta) {
253
277
  if (adpDelta === null)
254
278
  return 'FAIR';
@@ -391,25 +415,32 @@ export class DraftPickAnalysis extends AnalysisTemplate {
391
415
  }
392
416
  return counts;
393
417
  }
394
- resolvePlayoffWindow(settings) {
395
- const leagueMeta = settings?.fantasy_content?.league?.[0] ?? {};
396
- const leagueSettings = settings?.fantasy_content?.league?.[1]?.settings?.[0] ?? {};
397
- const startDate = leagueMeta.start_date;
398
- const endWeek = Number(leagueMeta.end_week ?? leagueSettings.end_week ?? 0);
399
- const playoffStartWeek = Number(leagueSettings.playoff_start_week ?? 0);
400
- if (!startDate || !playoffStartWeek || !endWeek || playoffStartWeek > endWeek) {
418
+ /**
419
+ * Resolve the fantasy playoff weeks to calendar dates.
420
+ *
421
+ * v4 has no league settings to read, so the weeks come from the caller and
422
+ * week 1 is anchored to the NHL season's own opening night. Without stated
423
+ * weeks the window is unresolved, and the schedule component stays neutral
424
+ * rather than silently scoring the wrong three weeks.
425
+ */
426
+ resolvePlayoffWindow(args) {
427
+ const startWeek = Number(args?.playoff_start_week ?? 0);
428
+ const endWeek = Number(args?.playoff_end_week ?? 0);
429
+ const seasonStart = NHL_SCHEDULE.getSeasonStartDate();
430
+ if (!startWeek || !endWeek || startWeek > endWeek || !seasonStart) {
401
431
  return { resolved: false, start: null, end: null, weeks: [] };
402
432
  }
403
- const week1Monday = NhlScheduleService.weekStart(startDate);
433
+ const week1Monday = NhlScheduleService.weekStart(seasonStart);
404
434
  const weeks = [];
405
- for (let week = playoffStartWeek; week <= endWeek; week++) {
435
+ for (let week = startWeek; week <= endWeek; week++) {
406
436
  weeks.push(NhlScheduleService.addDays(week1Monday, (week - 1) * 7));
407
437
  }
408
438
  return {
409
439
  resolved: true,
410
- playoff_start_week: playoffStartWeek,
440
+ week_1_anchor: `${week1Monday} (NHL season opener)`,
441
+ playoff_start_week: startWeek,
411
442
  end_week: endWeek,
412
- start: NhlScheduleService.addDays(week1Monday, (playoffStartWeek - 1) * 7),
443
+ start: NhlScheduleService.addDays(week1Monday, (startWeek - 1) * 7),
413
444
  end: NhlScheduleService.addDays(week1Monday, endWeek * 7 - 1),
414
445
  weeks
415
446
  };
package/build/index.js CHANGED
@@ -1218,8 +1218,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
1218
1218
  },
1219
1219
  pool_size: {
1220
1220
  type: "number",
1221
- description: "How deep to pull the player pool (default 150, max 300)",
1222
- default: 150
1221
+ description: "How deep to pull the player pool (default 250, max 400)",
1222
+ default: 250
1223
+ },
1224
+ playoff_start_week: {
1225
+ type: "number",
1226
+ description: "First week of your fantasy playoffs. Supply this with playoff_end_week and each club's playoff-window schedule becomes a tiebreaker between similar players."
1227
+ },
1228
+ playoff_end_week: {
1229
+ type: "number",
1230
+ description: "Final week of your fantasy playoffs (commonly your league's last week)."
1223
1231
  },
1224
1232
  ...baseChirpSchema
1225
1233
  }
@@ -1638,7 +1646,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1638
1646
  already_drafted: args?.already_drafted,
1639
1647
  roster_needs: args?.roster_needs,
1640
1648
  max_results: args?.max_results,
1641
- pool_size: args?.pool_size
1649
+ pool_size: args?.pool_size,
1650
+ playoff_start_week: args?.playoff_start_week,
1651
+ playoff_end_week: args?.playoff_end_week
1642
1652
  }, semanticContract);
1643
1653
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1644
1654
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@semanticintent/semantic-chirp-intelligence-mcp",
3
- "version": "4.0.1",
4
- "description": "Universal fantasy hockey intelligence a Model Context Protocol server that reads NHL schedule and player data and analyses any roster you paste. No account, no API key, no platform lock-in.",
3
+ "version": "4.0.3",
4
+ "description": "Universal fantasy hockey intelligence \u2014 a Model Context Protocol server that reads NHL schedule and player data and analyses any roster you paste. No account, no API key, no platform lock-in.",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
7
7
  "scripts": {
@@ -43,6 +43,6 @@
43
43
  "vitest": "^4.1.10"
44
44
  },
45
45
  "dependencies": {
46
- "@modelcontextprotocol/sdk": "^1.19.1"
46
+ "@modelcontextprotocol/sdk": "^1.30.0"
47
47
  }
48
48
  }