@semanticintent/semantic-chirp-intelligence-mcp 4.0.0 → 4.0.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.
@@ -20,5 +20,5 @@
20
20
  "position": "D"
21
21
  }
22
22
  ],
23
- "updated_at": "2026-09-01T20:15:07.802Z"
23
+ "updated_at": "2026-09-01T20:56:50.722Z"
24
24
  }
@@ -30,5 +30,5 @@
30
30
  "slot": "LW"
31
31
  }
32
32
  ],
33
- "updated_at": "2026-09-01T20:15:07.663Z"
33
+ "updated_at": "2026-09-01T20:56:50.579Z"
34
34
  }
@@ -13,5 +13,5 @@
13
13
  "points": 138
14
14
  }
15
15
  ],
16
- "updated_at": "2026-09-01T20:15:07.954Z"
16
+ "updated_at": "2026-09-01T20:56:50.874Z"
17
17
  }
package/README.md CHANGED
@@ -37,7 +37,7 @@ occasional chirp.
37
37
  - 🗓️ **Schedule intelligence** — every game count comes from the NHL's public club-schedule API, per club, per week
38
38
  - 🏛️ **Semantic Anchoring Governance** — every tool declares its intent; a dashboard surfaces the health metrics
39
39
  - 🧩 **Template Pattern architecture** — analyses are composable, consistent, and testable
40
- - 🔒 **Read-only & local** — OAuth 2.0, minimum permissions, no third-party data egress
40
+ - 🔒 **Read-only & local** — no credentials of any kind, no third-party data egress; your roster never leaves your machine
41
41
 
42
42
  ---
43
43
 
@@ -53,7 +53,8 @@ occasional chirp.
53
53
  ├─────────────────────────────────────────────────────────────┤
54
54
  │ Intelligence layer (src/analyses/ + src/template/) │
55
55
  │ • AnalysisTemplate — shared Template Pattern base │
56
- │ • Ice / Streaming / GamesInHand / WeekendStream / Lineup
56
+ │ • Ice / Lineup / GamesInHand / Streaming / WeekendStream
57
+ │ • Breakout / ScheduleValue / DraftPick │
57
58
  ├─────────────────────────────────────────────────────────────┤
58
59
  │ Services (src/services/) │
59
60
  │ • ChirpIntelligence — turns data into chirp │
@@ -174,10 +175,7 @@ Edit your Claude Desktop config:
174
175
  "mcpServers": {
175
176
  "semantic-chirp-intelligence-mcp": {
176
177
  "command": "node",
177
- "args": ["/absolute/path/to/semantic-chirp-intelligence-mcp/build/index.js"],
178
- "env": {
179
- "DOTENV_CONFIG_QUIET": "true"
180
- }
178
+ "args": ["/absolute/path/to/semantic-chirp-intelligence-mcp/build/index.js"]
181
179
  }
182
180
  }
183
181
  }
@@ -185,11 +183,9 @@ Edit your Claude Desktop config:
185
183
 
186
184
  Use an **absolute path** to `build/index.js` (forward slashes, even on Windows). Restart Claude Desktop — the CHIRP tools will appear.
187
185
 
188
- Your credentials stay in the project's git-ignored `.env`; the server resolves
189
- it from its own install directory, so it is found regardless of the working
190
- directory the client launches it with. You do **not** need to copy secrets into
191
- the client config. If you prefer to set them there anyway, a client `env` block
192
- still overrides the file.
186
+ There is no `env` block, because there is nothing to configure. Your pasted
187
+ roster is stored in the project's git-ignored `.chirp-data/` and never leaves
188
+ your machine.
193
189
 
194
190
  ---
195
191
 
@@ -203,7 +199,7 @@ Once connected, just talk to Claude about your team:
203
199
  - *"Where do I have a games-in-hand edge over my opponent?"*
204
200
  - *"Find me streaming goalies for the weekend — real value, not desperation pickups."*
205
201
  - *"Am I winning my matchup? Which categories am I losing?"*
206
- - *"Who are the hottest waiver adds in my league?"*
202
+ - *"Who are the best players not on my roster right now?"*
207
203
  - *"Show me the governance dashboard."*
208
204
 
209
205
  ---
@@ -232,7 +228,7 @@ semantic-chirp-intelligence-mcp/
232
228
  │ ├── services/ # NhlScheduleService, NhlStatsService, RosterStore,
233
229
  │ │ # LeagueDataService, ChirpIntelligence
234
230
  │ ├── config/ # tool-metadata, personality-modes, chirp-styles
235
- │ ├── domain/ # types, governance, nhl-teams, yahoo-stats
231
+ │ ├── domain/ # types, governance, nhl-teams
236
232
  ├── tests/ # vitest tests
237
233
  ├── scripts/preflight.mjs # build + NHL API check
238
234
  ├── scripts/smoke.mjs # calls every tool, flags crash-like responses
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@semanticintent/semantic-chirp-intelligence-mcp",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
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",
@@ -43,7 +43,6 @@
43
43
  "vitest": "^4.1.10"
44
44
  },
45
45
  "dependencies": {
46
- "@modelcontextprotocol/sdk": "^1.19.1",
47
- "dotenv": "^17.4.2"
46
+ "@modelcontextprotocol/sdk": "^1.19.1"
48
47
  }
49
48
  }