rewind-mcp-server 0.7.0 → 0.8.0

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.
@@ -0,0 +1,565 @@
1
+ import { z } from 'zod';
2
+ import { withRichResponse, text, imageBlock, formatDate, fmt, READ_ONLY_ANNOTATIONS, includeImagesParam, LIST_IMAGE_PX, } from './helpers.js';
3
+ // ─── Tool registration ───────────────────────────────────────────────
4
+ export function registerAttendingTools(server, client) {
5
+ // get_attended_events ─────────────────────────────────────────────
6
+ server.tool('get_attended_events', 'List events you bought tickets for: sports games, concerts, theater. Filterable by category (sports/music/arts), event_type (mlb_game, concert, etc.), season, year, venue, and team. Includes events you bought tickets for but did not attend (attended=false). Use `team` (substring match like "mariners" or "huskies") for natural-language queries; `team_id` for stable integer-keyed lookups. When the user asks about a SINGLE specific event ("last Mariners game I went to", "the Springsteen show I attended"), call this with the appropriate filter + `limit: 1` to find the id, then follow up with `get_attended_event(id)` to render the rich inline card — do not stop at the list-tool text response.', {
7
+ page: z.number().min(1).default(1).describe('Page number, 1-indexed.'),
8
+ limit: z
9
+ .number()
10
+ .min(1)
11
+ .max(100)
12
+ .default(20)
13
+ .describe('Items per page (max 100).'),
14
+ category: z
15
+ .enum(['sports', 'music', 'arts'])
16
+ .optional()
17
+ .describe('Top-level category filter.'),
18
+ event_type: z
19
+ .string()
20
+ .optional()
21
+ .describe('Specific type, e.g. "mlb_game", "concert", "ncaaf_game".'),
22
+ season: z
23
+ .number()
24
+ .int()
25
+ .optional()
26
+ .describe('Sports season year (e.g. 2024).'),
27
+ year: z
28
+ .number()
29
+ .int()
30
+ .optional()
31
+ .describe('Calendar year filter on event_date.'),
32
+ venue_id: z.number().int().optional().describe('Filter by venue id.'),
33
+ attended: z
34
+ .number()
35
+ .int()
36
+ .min(0)
37
+ .max(1)
38
+ .optional()
39
+ .describe('1 = only attended, 0 = only unattended (purchased but missed). Omit to return both.'),
40
+ team: z
41
+ .string()
42
+ .optional()
43
+ .describe('Case-insensitive substring match against either team name in event_data. e.g. "mariners", "huskies", "storm". Returns games where this team was either home or away.'),
44
+ team_id: z
45
+ .number()
46
+ .int()
47
+ .optional()
48
+ .describe('Exact match on the league-native team id. e.g. 136 = Seattle Mariners (MLB), 264 = Washington Huskies (ESPN). Use when the natural-language `team` substring is ambiguous.'),
49
+ }, READ_ONLY_ANNOTATIONS, async ({ page, limit, category, event_type, season, year, venue_id, attended, team, team_id, }) => withRichResponse(async () => {
50
+ const data = await client.get('/attending/events', {
51
+ page,
52
+ limit,
53
+ category,
54
+ event_type,
55
+ season,
56
+ year,
57
+ venue_id,
58
+ attended,
59
+ team,
60
+ team_id,
61
+ });
62
+ if (!data.data.length) {
63
+ return {
64
+ content: [text('No attended events match those filters.')],
65
+ structuredContent: data,
66
+ };
67
+ }
68
+ const lines = [
69
+ `Attended events (page ${page} of ${data.pagination.total_pages}, ${data.pagination.total} total):`,
70
+ ];
71
+ for (const [i, e] of data.data.entries()) {
72
+ const date = formatDate(e.event_date);
73
+ const venue = e.venue ? ` @ ${e.venue.name}` : '';
74
+ const score = e.subtitle ? ` -- ${e.subtitle}` : '';
75
+ const noShow = e.attended ? '' : ' [no-show]';
76
+ lines.push(`${i + 1}. ${date} -- ${e.title}${venue}${score}${noShow}`);
77
+ }
78
+ return {
79
+ content: [text(lines.join('\n'))],
80
+ structuredContent: data,
81
+ };
82
+ }));
83
+ // get_attended_season ─────────────────────────────────────────────
84
+ // Drives the season-grid card UI in MCP Apps hosts.
85
+ server.registerTool('get_attended_season', {
86
+ title: 'Attended sports season',
87
+ description: 'Get every game you attended (or hold tickets for) in a given league + season, with W/L record. league is a slug like "mlb", "nfl", "ncaaf", "nba", "wnba". In MCP Apps hosts, renders an interactive season grid with score, attendance, and notable performers.',
88
+ inputSchema: {
89
+ league: z
90
+ .string()
91
+ .describe('League slug (lowercase): "mlb", "nfl", "nba", "wnba", "ncaaf", "ncaab", "mls".'),
92
+ season: z
93
+ .number()
94
+ .int()
95
+ .describe('Season year (e.g. 2024 for the 2024 MLB season).'),
96
+ },
97
+ annotations: READ_ONLY_ANNOTATIONS,
98
+ _meta: {
99
+ ui: { resourceUri: 'ui://rewind/attended-season.html' },
100
+ 'ui/resourceUri': 'ui://rewind/attended-season.html',
101
+ },
102
+ }, async ({ league, season }) => withRichResponse(async () => {
103
+ const data = await client.get(`/attending/seasons/${league}/${season}`);
104
+ if (!data.data.length) {
105
+ return {
106
+ content: [
107
+ text(`No attended ${league.toUpperCase()} games in ${season}.`),
108
+ ],
109
+ structuredContent: data,
110
+ };
111
+ }
112
+ const lines = [
113
+ `${league.toUpperCase()} ${season}: ${data.attended_count} games attended (${data.wins}-${data.losses})`,
114
+ '',
115
+ ];
116
+ for (const e of data.data) {
117
+ const date = formatDate(e.event_date);
118
+ const venue = e.venue ? ` @ ${e.venue.name}` : '';
119
+ const score = e.subtitle ? ` -- ${e.subtitle}` : '';
120
+ const noShow = e.attended ? '' : ' [no-show]';
121
+ lines.push(`${date}: ${e.title}${venue}${score}${noShow}`);
122
+ }
123
+ return {
124
+ content: [text(lines.join('\n'))],
125
+ structuredContent: data,
126
+ };
127
+ }));
128
+ // get_attended_player ─────────────────────────────────────────────
129
+ // get_attended_players ────────────────────────────────────────────
130
+ // Search across the players-you-have-watched-play list. Supports name
131
+ // substring lookup so the model can resolve "Julio" -> player id without
132
+ // the user having to know the integer id.
133
+ server.tool('get_attended_players', 'Search the list of players (MLB, NFL, NCAAF, NBA, etc.) you have watched play in person. Use `name` (substring, case-insensitive) to resolve a player by name. Use `league` and/or `team_id` to filter further. Common names like "Will Smith" return multiple matches — disambiguate via `primary_team.abbreviation` and `primary_position` on each result without a follow-up turn. When the user asks about a SPECIFIC player ("how\'s JP Crawford playing this year", "what are Cal Raleigh\'s stats", "tell me about Kirby"), call this to resolve the name to an id, then follow up with `get_attended_player(id)` to render the rich inline athlete card with current-season stats — do not stop at the search-result text response.', {
134
+ page: z
135
+ .number()
136
+ .int()
137
+ .min(1)
138
+ .default(1)
139
+ .describe('Page number, 1-indexed.'),
140
+ limit: z
141
+ .number()
142
+ .int()
143
+ .min(1)
144
+ .max(50)
145
+ .default(10)
146
+ .describe('Items per page (max 50).'),
147
+ name: z
148
+ .string()
149
+ .optional()
150
+ .describe('Case-insensitive substring match on full name. e.g. "julio", "kirby", "will smith".'),
151
+ league: z
152
+ .string()
153
+ .optional()
154
+ .describe('Filter by league slug, e.g. "mlb", "nfl", "ncaaf".'),
155
+ team_id: z
156
+ .number()
157
+ .int()
158
+ .optional()
159
+ .describe('Filter by primary team id (league-native, e.g. 136 = Mariners in MLB).'),
160
+ }, READ_ONLY_ANNOTATIONS, async ({ page, limit, name, league, team_id }) => withRichResponse(async () => {
161
+ const data = await client.get('/attending/players', {
162
+ page,
163
+ limit,
164
+ name,
165
+ league,
166
+ team_id,
167
+ });
168
+ if (!data.data.length) {
169
+ return {
170
+ content: [text('No players match those filters.')],
171
+ structuredContent: data,
172
+ };
173
+ }
174
+ const lines = [
175
+ `Players (${data.data.length} of ${data.pagination.total} matching${name ? ` "${name}"` : ''}):`,
176
+ ];
177
+ for (const p of data.data) {
178
+ const pos = p.primary_position ? ` ${p.primary_position}` : '';
179
+ const num = p.primary_number ? ` #${p.primary_number}` : '';
180
+ const team = p.primary_team ? ` ${p.primary_team.abbreviation}` : '';
181
+ lines.push(`id=${p.id} ${p.full_name}${num}${pos} (${p.league}${team})`);
182
+ }
183
+ return {
184
+ content: [text(lines.join('\n'))],
185
+ structuredContent: data,
186
+ };
187
+ }));
188
+ // get_attended_player ───────────────────────────────────────────
189
+ // Registered via server.registerTool so we can attach `_meta.ui.resourceUri`.
190
+ // Hosts that support MCP Apps render the athlete card inline; others fall
191
+ // back to the text + photo response.
192
+ //
193
+ // structuredContent uses the DESIGN.md nested shape: { player, supported,
194
+ // season_stats, attended_summary, attended_appearances, attended_appearance_count }.
195
+ // Appearances are capped at 10 most recent to keep the response within the
196
+ // 8 KB token budget.
197
+ server.registerTool('get_attended_player', {
198
+ title: 'Athlete — detail',
199
+ description: 'Detailed athlete card for an MLB / NFL / NCAAF / NBA player you\'ve watched play in person. **Use this whenever the user asks how a specific player is performing this season, what their batting average / ERA / current stats are, how their career has gone, or how they\'ve played in the games you attended** — e.g. "how\'s JP Crawford playing this year", "what are Cal Raleigh\'s numbers", "show me Kirby\'s stats", "tell me about Julio Rodriguez". Returns bio (position, jersey, debut, height/weight, college, awards), team logo, current-season stats (live MLB Stats API for MLB players, KV-cached 1h), career-by-season table, home/away/L-R splits, your-attended summary aggregated across every attended game, and the 10 most recent attended appearances with their stat lines. MLB-only for the live-stats panel — non-MLB players surface as supported:false. In MCP Apps hosts, renders the rich inline athlete card. If you do not have the player id, first call `get_attended_players` with `name` to resolve the id, then call this to render the card.',
200
+ inputSchema: {
201
+ id: z.number().int().describe('Player id.'),
202
+ ...includeImagesParam,
203
+ },
204
+ annotations: READ_ONLY_ANNOTATIONS,
205
+ _meta: {
206
+ ui: { resourceUri: 'ui://rewind/attended-player.html' },
207
+ 'ui/resourceUri': 'ui://rewind/attended-player.html',
208
+ },
209
+ }, async ({ id, include_images }) => withRichResponse(async () => {
210
+ const data = await client.get(`/attending/players/${id}`);
211
+ const bio = [
212
+ `${data.full_name}${data.primary_number ? ` #${data.primary_number}` : ''}${data.primary_position ? ` (${data.primary_position})` : ''}`,
213
+ data.primary_team
214
+ ? `Team: ${data.primary_team.full_name ?? data.primary_team.name} (${data.primary_team.abbreviation})`
215
+ : null,
216
+ data.bats || data.throws
217
+ ? `Bats: ${data.bats ?? '?'}, Throws: ${data.throws ?? '?'}`
218
+ : null,
219
+ data.debut_date ? `MLB debut: ${formatDate(data.debut_date)}` : null,
220
+ data.height || data.weight
221
+ ? `${data.height ?? ''}${data.weight ? `, ${data.weight} lbs` : ''}`
222
+ .trim()
223
+ .replace(/^,\s*/, '')
224
+ : null,
225
+ data.birth_city || data.birth_state_province || data.birth_country
226
+ ? `From: ${[data.birth_city, data.birth_state_province, data.birth_country].filter(Boolean).join(', ')}`
227
+ : null,
228
+ data.college_name ? `College: ${data.college_name}` : null,
229
+ ].filter((l) => l !== null);
230
+ const lines = [bio.join('\n')];
231
+ // This-season stats — for MLB hitters/pitchers.
232
+ if (data.season_stats?.hitter) {
233
+ const h = data.season_stats.hitter;
234
+ lines.push('', `${data.season_stats.season} season: .${(h.avg ?? '.000').toString().replace(/^\./, '')} / .${(h.slg ?? '.000').toString().replace(/^\./, '')} (AVG / SLG), ${h.hr ?? 0} HR, ${h.rbi ?? 0} RBI in ${h.games_played ?? 0} games`);
235
+ }
236
+ else if (data.season_stats?.pitcher) {
237
+ const p = data.season_stats.pitcher;
238
+ lines.push('', `${data.season_stats.season} season: ${p.era ?? '0.00'} ERA, ${p.whip ?? '0.00'} WHIP, ${p.k ?? 0} K in ${p.ip ?? '0'} IP`);
239
+ }
240
+ // Attended summary — your stat line from games you attended.
241
+ if (data.attended_summary.hitter) {
242
+ const h = data.attended_summary.hitter;
243
+ lines.push('', `In ${data.attended_summary.games_attended} games you attended: ${h.h ?? 0} hits in ${h.ab ?? 0} AB, ${h.hr ?? 0} HR, ${h.rbi ?? 0} RBI`);
244
+ }
245
+ else if (data.attended_summary.pitcher) {
246
+ const p = data.attended_summary.pitcher;
247
+ const dec = p.decisions;
248
+ lines.push('', `In ${data.attended_summary.games_attended} games you attended: ${p.ip ?? '0'} IP, ${p.k ?? 0} K, ${p.era ?? '0.00'} ERA${dec ? ` (${dec.w ?? 0}-${dec.l ?? 0})` : ''}`);
249
+ }
250
+ if (data.appearance_count > 0) {
251
+ lines.push('', `${data.appearance_count} attended appearance${data.appearance_count === 1 ? '' : 's'}:`);
252
+ for (const a of data.appearances.slice(0, 25)) {
253
+ const date = formatDate(a.event_date);
254
+ const stat = summarizeAppearance(a);
255
+ const decision = a.decision ? ` (${a.decision})` : '';
256
+ lines.push(`${date}: ${a.title}${decision} -- ${stat}`);
257
+ }
258
+ if (data.appearance_count > 25) {
259
+ lines.push(`... and ${data.appearance_count - 25} more.`);
260
+ }
261
+ }
262
+ const images = [];
263
+ if (include_images) {
264
+ const silo = await imageBlock(client, data.photo_silo, LIST_IMAGE_PX);
265
+ if (silo)
266
+ images.push(silo);
267
+ }
268
+ // structuredContent: nested DESIGN.md shape. Appearances capped at
269
+ // 10 most recent for the card; total surfaced via attended_appearance_count.
270
+ const structuredContent = {
271
+ player: {
272
+ id: data.id,
273
+ mlb_stats_id: data.mlb_stats_id,
274
+ full_name: data.full_name,
275
+ primary_position: data.primary_position,
276
+ primary_number: data.primary_number,
277
+ bats: data.bats,
278
+ throws: data.throws,
279
+ debut_date: data.debut_date,
280
+ birth_date: data.birth_date,
281
+ birth_city: data.birth_city,
282
+ birth_state_province: data.birth_state_province,
283
+ birth_country: data.birth_country,
284
+ height: data.height,
285
+ weight: data.weight,
286
+ college_name: data.college_name,
287
+ awards: data.awards,
288
+ photo_silo: data.photo_silo,
289
+ photo_full: data.photo_full,
290
+ league: data.league,
291
+ primary_team: data.primary_team,
292
+ },
293
+ supported: data.supported,
294
+ season_stats: data.season_stats,
295
+ career: data.career,
296
+ splits: data.splits,
297
+ attended_summary: data.attended_summary,
298
+ attended_appearances: data.appearances.slice(0, 10).map((a) => ({
299
+ event_id: a.event_id,
300
+ event_date: a.event_date,
301
+ title: a.title,
302
+ is_home: a.is_home,
303
+ batting_line: a.batting_line,
304
+ pitching_line: a.pitching_line,
305
+ decision: a.decision,
306
+ notable: a.notable,
307
+ // Notable reasons stitched from batting/pitching lines for the
308
+ // card. Lightweight client-side derivation matches the season
309
+ // grid card's existing pattern.
310
+ notable_reasons: deriveNotableReasons(a),
311
+ })),
312
+ attended_appearance_count: data.appearance_count,
313
+ };
314
+ return {
315
+ content: [text(lines.join('\n')), ...images],
316
+ structuredContent,
317
+ };
318
+ }));
319
+ server.tool('get_attended_player_stats', 'Aggregate stats for one player across the games you attended. MLB players get a hitter slash line + counting stats, or a pitcher line + ERA / WHIP / decisions, depending on which stat lines exist on their appearances. Non-MLB players (NFL, NCAAF, NBA, etc.) return supported=false with appearance summaries (final scores, opponents) — full stat-line parsing for those leagues is on the roadmap. \n\n**Use career (omit `season`) by default.** Single-season slices are tiny — max 50 PAs across the entire dataset; career is where meaningful samples live (Cal Raleigh ~130 PAs / 32 attended games; Kirby ~238 BFs / 10 attended starts). Always cite `pa` (hitter) or `bf` (pitcher) and `games` when phrasing the answer so the user can judge confidence.', {
320
+ id: z
321
+ .number()
322
+ .int()
323
+ .describe('Player id (from get_attended_players or get_attended_player).'),
324
+ season: z
325
+ .number()
326
+ .int()
327
+ .optional()
328
+ .describe('Optional. Single-season slice. Omit for career across all attended games (recommended — see tool description).'),
329
+ }, READ_ONLY_ANNOTATIONS, async ({ id, season }) => withRichResponse(async () => {
330
+ const data = await client.get(`/attending/players/${id}/stats`, season !== undefined ? { season } : {});
331
+ const scopeLabel = data.scope === 'career'
332
+ ? 'across all attended games'
333
+ : `in ${data.season}`;
334
+ if (data.supported === false) {
335
+ const games = data.appearances.length;
336
+ const lines = [
337
+ `${data.player.full_name} (${data.league}) — ${games} attended game${games === 1 ? '' : 's'} ${scopeLabel}.`,
338
+ 'Per-player stat-line parsing is not yet supported for this league.',
339
+ '',
340
+ 'Game appearances:',
341
+ ];
342
+ for (const a of data.appearances.slice(0, 25)) {
343
+ const date = formatDate(a.event_date);
344
+ const score = a.final_score ? ` (${a.final_score})` : '';
345
+ const result = a.my_team_won === true
346
+ ? ' W'
347
+ : a.my_team_won === false
348
+ ? ' L'
349
+ : '';
350
+ lines.push(` ${date}: ${a.title}${score}${result}`);
351
+ }
352
+ if (data.appearances.length > 25) {
353
+ lines.push(` ... and ${data.appearances.length - 25} more.`);
354
+ }
355
+ return {
356
+ content: [text(lines.join('\n'))],
357
+ structuredContent: data,
358
+ };
359
+ }
360
+ if (data.batting) {
361
+ const b = data.batting;
362
+ const lines = [
363
+ `${data.player.full_name} ${scopeLabel} — ${data.games} attended game${data.games === 1 ? '' : 's'}, ${b.pa} PA${b.pa === 1 ? '' : 's'}.`,
364
+ `Slash: ${b.avg ?? '—'} / ${b.slg ?? '—'} (AVG / SLG; OBP not stored)`,
365
+ `Counting: ${b.h} H, ${b.hr} HR, ${b.rbi} RBI, ${b.bb} BB, ${b.k} K, ${b.sb} SB`,
366
+ data.games_with_box_score < data.games
367
+ ? `Note: ${data.games - data.games_with_box_score} of ${data.games} attended games lack box-score data.`
368
+ : null,
369
+ ].filter((l) => l !== null);
370
+ return {
371
+ content: [text(lines.join('\n'))],
372
+ structuredContent: data,
373
+ };
374
+ }
375
+ if (data.pitching) {
376
+ const p = data.pitching;
377
+ const dec = `${p.decisions.w}W-${p.decisions.l}L${p.decisions.sv ? `, ${p.decisions.sv} SV` : ''}${p.decisions.hld ? `, ${p.decisions.hld} HLD` : ''}${p.decisions.bs ? `, ${p.decisions.bs} BS` : ''}`;
378
+ const lines = [
379
+ `${data.player.full_name} ${scopeLabel} — ${data.games} attended game${data.games === 1 ? '' : 's'}, ${p.bf} BF.`,
380
+ `${p.ip} IP, ${p.er} ER, ${p.k} K, ${p.bb} BB`,
381
+ `${p.era ?? '—'} ERA, ${p.whip ?? '—'} WHIP`,
382
+ `Decisions: ${dec}`,
383
+ data.games_with_box_score < data.games
384
+ ? `Note: ${data.games - data.games_with_box_score} of ${data.games} attended games lack box-score data.`
385
+ : null,
386
+ ].filter((l) => l !== null);
387
+ return {
388
+ content: [text(lines.join('\n'))],
389
+ structuredContent: data,
390
+ };
391
+ }
392
+ // MLB player with attended games but no batting/pitching lines.
393
+ return {
394
+ content: [
395
+ text(`${data.player.full_name} attended ${data.games} game${data.games === 1 ? '' : 's'} ${scopeLabel}, but no per-player stat lines exist for this player on those games.`),
396
+ ],
397
+ structuredContent: data,
398
+ };
399
+ }));
400
+ // get_attending_stats ─────────────────────────────────────────────
401
+ server.tool('get_attending_stats', 'Aggregate counts of attended events broken down by category, event_type, and year.', {}, READ_ONLY_ANNOTATIONS, async () => withRichResponse(async () => {
402
+ const data = await client.get('/attending/stats');
403
+ const lines = [
404
+ `Total events: ${fmt(data.total_events)} (${fmt(data.attended_events)} attended)`,
405
+ '',
406
+ 'By category:',
407
+ ...data.by_category.map((r) => ` ${r.category}: ${fmt(r.count)}`),
408
+ '',
409
+ 'By event type:',
410
+ ...data.by_event_type.map((r) => ` ${r.event_type}: ${fmt(r.count)}`),
411
+ '',
412
+ 'By year:',
413
+ ...data.by_year
414
+ .slice(0, 15)
415
+ .map((r) => ` ${r.year}: ${fmt(r.count)}`),
416
+ ];
417
+ return {
418
+ content: [text(lines.join('\n'))],
419
+ structuredContent: data,
420
+ };
421
+ }));
422
+ // get_attended_event ──────────────────────────────────────────────
423
+ // Uses server.registerTool so we can attach _meta.ui.resourceUri.
424
+ // MCP Apps hosts (Claude Desktop, Claude web, Claude iOS) render the
425
+ // game card inline via ui://rewind/attended-event.html; non-MCP-Apps
426
+ // clients fall back to the text + structuredContent response.
427
+ server.registerTool('get_attended_event', {
428
+ title: 'Attended event',
429
+ description: 'Get a single attended event (sports game, concert, theater show) in full detail, including venue, tickets, and per-player stat lines for sports games. Renders the rich inline event card — linescore, top performers with photos, ticket info — in MCP Apps hosts. Use this when the user asks about ONE specific event: "tell me about my last Mariners game," "who pitched in that Phillies game," "the Springsteen show I went to," "what happened at that game." If you do not have the event id, first call `get_attended_events` with a `team` / `event_type` filter (and `limit: 1` if the user asked for the most recent) to find the id, then call this to render the card.',
430
+ inputSchema: {
431
+ id: z.number().int().describe('Event id.'),
432
+ },
433
+ annotations: READ_ONLY_ANNOTATIONS,
434
+ _meta: {
435
+ ui: { resourceUri: 'ui://rewind/attended-event.html' },
436
+ 'ui/resourceUri': 'ui://rewind/attended-event.html',
437
+ },
438
+ }, async ({ id }) => withRichResponse(async () => {
439
+ const data = await client.get(`/attending/events/${id}`);
440
+ const date = formatDate(data.event_date);
441
+ const venue = data.venue ? ` @ ${data.venue.name}` : '';
442
+ const score = data.subtitle ? ` -- ${data.subtitle}` : '';
443
+ const lines = [`${date}: ${data.title}${venue}${score}`];
444
+ if (data.event_data) {
445
+ const ed = data.event_data;
446
+ if (ed.attendance)
447
+ lines.push(`Attendance: ${fmt(ed.attendance)}`);
448
+ if (ed.weather && typeof ed.weather === 'object') {
449
+ const w = ed.weather;
450
+ const parts = [
451
+ w.condition,
452
+ w.temp ? `${w.temp}°F` : null,
453
+ w.wind,
454
+ ].filter(Boolean);
455
+ if (parts.length)
456
+ lines.push(`Weather: ${parts.join(', ')}`);
457
+ }
458
+ if (ed.duration_minutes)
459
+ lines.push(`Duration: ${ed.duration_minutes} min`);
460
+ }
461
+ const notable = data.players.filter((p) => p.notable);
462
+ if (notable.length) {
463
+ lines.push('', 'Notable performances:');
464
+ for (const a of notable.slice(0, 12)) {
465
+ const stat = summarizeAppearance(a);
466
+ const decision = a.decision ? ` (${a.decision})` : '';
467
+ lines.push(` ${a.player.full_name}${decision} -- ${stat}`);
468
+ }
469
+ }
470
+ return {
471
+ content: [text(lines.join('\n'))],
472
+ structuredContent: data,
473
+ };
474
+ }));
475
+ // get_attending_year_in_review ────────────────────────────────────
476
+ server.tool('get_attending_year_in_review', 'Year-in-review summary for attended events: totals, monthly breakdown, top venues, top concert performers, and the full event list. Use this when the user asks "what shows did I see in 2024" or "best year for games".', {
477
+ year: z.number().int().describe('Calendar year, e.g. 2024.'),
478
+ }, READ_ONLY_ANNOTATIONS, async ({ year }) => withRichResponse(async () => {
479
+ const data = await client.get(`/attending/year/${year}`);
480
+ const lines = [
481
+ `${data.year}: ${fmt(data.total_events)} attended events`,
482
+ ];
483
+ if (data.total_spent_cents > 0) {
484
+ lines.push(`Total ticket spend: $${(data.total_spent_cents / 100).toFixed(2)}`);
485
+ }
486
+ if (data.by_event_type.length) {
487
+ lines.push('', 'By event type:');
488
+ for (const r of data.by_event_type) {
489
+ lines.push(` ${r.event_type}: ${fmt(r.count)}`);
490
+ }
491
+ }
492
+ if (data.top_venues.length) {
493
+ lines.push('', 'Top venues:');
494
+ for (const v of data.top_venues) {
495
+ const city = v.city ? ` (${v.city})` : '';
496
+ lines.push(` ${v.name}${city}: ${fmt(v.count)}`);
497
+ }
498
+ }
499
+ if (data.top_performers.length) {
500
+ lines.push('', 'Top performers:');
501
+ for (const p of data.top_performers) {
502
+ lines.push(` ${p.name}: ${fmt(p.count)}`);
503
+ }
504
+ }
505
+ return {
506
+ content: [text(lines.join('\n'))],
507
+ structuredContent: data,
508
+ };
509
+ }));
510
+ }
511
+ // ─── Helpers ─────────────────────────────────────────────────────────
512
+ // Reasons a particular attended appearance is "notable" — feeds the
513
+ // athlete card's bullet highlights ("3 HRs you witnessed live", etc.).
514
+ // Cheap and stateless; matches the criteria used by the per-game
515
+ // notable=1 backend flag.
516
+ function deriveNotableReasons(a) {
517
+ const reasons = [];
518
+ if (a.batting_line) {
519
+ const b = a.batting_line;
520
+ if ((b.hr ?? 0) > 0)
521
+ reasons.push(`${b.hr} HR`);
522
+ if ((b.h ?? 0) >= 3)
523
+ reasons.push('multi-hit');
524
+ if ((b.rbi ?? 0) >= 4)
525
+ reasons.push(`${b.rbi} RBI`);
526
+ if ((b.sb ?? 0) >= 2)
527
+ reasons.push(`${b.sb} SB`);
528
+ }
529
+ if (a.pitching_line) {
530
+ const p = a.pitching_line;
531
+ const ipNum = parseFloat(p.ip ?? '0');
532
+ if (ipNum >= 9)
533
+ reasons.push('complete game');
534
+ if ((p.k ?? 0) >= 10)
535
+ reasons.push(`${p.k} K`);
536
+ }
537
+ if (a.decision === 'W')
538
+ reasons.push('win');
539
+ if (a.decision === 'SV')
540
+ reasons.push('save');
541
+ return reasons;
542
+ }
543
+ function summarizeAppearance(a) {
544
+ const parts = [];
545
+ if (a.batting_line) {
546
+ const b = a.batting_line;
547
+ const line = `${b.h ?? 0}-for-${b.ab ?? 0}`;
548
+ const extras = [];
549
+ if (b.hr)
550
+ extras.push(`${b.hr} HR`);
551
+ if (b.rbi)
552
+ extras.push(`${b.rbi} RBI`);
553
+ if (b.bb)
554
+ extras.push(`${b.bb} BB`);
555
+ if (b.k)
556
+ extras.push(`${b.k} K`);
557
+ parts.push(extras.length ? `${line}, ${extras.join(', ')}` : line);
558
+ }
559
+ if (a.pitching_line) {
560
+ const p = a.pitching_line;
561
+ parts.push(`${p.ip ?? '0.0'} IP, ${p.h ?? 0} H, ${p.er ?? 0} ER, ${p.bb ?? 0} BB, ${p.k ?? 0} K`);
562
+ }
563
+ return parts.length ? parts.join(' | ') : '-';
564
+ }
565
+ //# sourceMappingURL=attending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attending.js","sourceRoot":"","sources":["../../src/tools/attending.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,7 @@ function rewindUri(domain, entityType, entityId) {
25
25
  }
26
26
  export function registerCrossDomainTools(server, client) {
27
27
  // search ─────────────────────────────────────────────────────────
28
- server.tool('search', 'Search across all domains (listening, running, watching, collecting, reading). Three ranking modes: keyword (default) uses FTS5 full-text search across all domains; semantic uses Voyage AI embeddings for paraphrased / meaning-based recall (reading domain only); hybrid fuses both (reading domain only). Use semantic or hybrid when the user describes what an article was ABOUT rather than quoting its words.', {
28
+ server.tool('search', 'Search across all domains (listening, running, watching, collecting, reading). Three ranking modes: keyword (default) uses FTS5 full-text search across all domains; semantic uses Voyage AI embeddings for paraphrased / meaning-based recall (reading domain only); hybrid fuses both (reading domain only). Use semantic or hybrid when the user describes what an article was ABOUT rather than quoting its words. When a result is the article the user is asking about, follow up with `get_article(id)` to render the rich inline article card — do not stop at the search-result text response.', {
29
29
  query: z.string().describe('Search query text'),
30
30
  domain: z
31
31
  .enum(['listening', 'running', 'watching', 'collecting', 'reading'])
@@ -125,7 +125,7 @@ export function registerCrossDomainTools(server, client) {
125
125
  };
126
126
  }));
127
127
  // semantic_search ────────────────────────────────────────────────
128
- server.tool('semantic_search', 'Semantic search over the reading domain using Voyage AI embeddings. Use when the user describes the gist or topic of an article they remember rather than quoting exact words — e.g. "article about a former SNL writer" or "piece about tech layoffs". Returns articles ranked by cosine similarity with a score in [0,1]. Reading domain only.', {
128
+ server.tool('semantic_search', 'Semantic search over the reading domain using Voyage AI embeddings. Use when the user describes the gist or topic of an article they remember rather than quoting exact words — e.g. "article about a former SNL writer" or "piece about tech layoffs". Returns articles ranked by cosine similarity with a score in [0,1]. Reading domain only. When the top result is the article the user is asking about, follow up with `get_article(id)` to render the rich inline article card; if the top score is low (~0.4 and below) or you are unsure which match is right, list the top 2-3 candidates and ask the user to disambiguate before rendering.', {
129
129
  query: z.string().describe('Natural-language description of the article'),
130
130
  limit: z
131
131
  .number()