rewind-mcp-server 0.1.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.
Files changed (47) hide show
  1. package/README.md +140 -0
  2. package/dist/client.d.ts +21 -0
  3. package/dist/client.js +62 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/consent.d.ts +10 -0
  6. package/dist/consent.js +148 -0
  7. package/dist/consent.js.map +1 -0
  8. package/dist/github-auth.d.ts +21 -0
  9. package/dist/github-auth.js +60 -0
  10. package/dist/github-auth.js.map +1 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.js +21 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/prompts.d.ts +2 -0
  15. package/dist/prompts.js +93 -0
  16. package/dist/prompts.js.map +1 -0
  17. package/dist/resources.d.ts +3 -0
  18. package/dist/resources.js +80 -0
  19. package/dist/resources.js.map +1 -0
  20. package/dist/server.d.ts +7 -0
  21. package/dist/server.js +59 -0
  22. package/dist/server.js.map +1 -0
  23. package/dist/tools/collecting.d.ts +3 -0
  24. package/dist/tools/collecting.js +92 -0
  25. package/dist/tools/collecting.js.map +1 -0
  26. package/dist/tools/cross-domain.d.ts +3 -0
  27. package/dist/tools/cross-domain.js +114 -0
  28. package/dist/tools/cross-domain.js.map +1 -0
  29. package/dist/tools/helpers.d.ts +32 -0
  30. package/dist/tools/helpers.js +69 -0
  31. package/dist/tools/helpers.js.map +1 -0
  32. package/dist/tools/listening.d.ts +3 -0
  33. package/dist/tools/listening.js +176 -0
  34. package/dist/tools/listening.js.map +1 -0
  35. package/dist/tools/reading.d.ts +3 -0
  36. package/dist/tools/reading.js +85 -0
  37. package/dist/tools/reading.js.map +1 -0
  38. package/dist/tools/running.d.ts +3 -0
  39. package/dist/tools/running.js +135 -0
  40. package/dist/tools/running.js.map +1 -0
  41. package/dist/tools/watching.d.ts +3 -0
  42. package/dist/tools/watching.js +138 -0
  43. package/dist/tools/watching.js.map +1 -0
  44. package/dist/worker.d.ts +20 -0
  45. package/dist/worker.js +173 -0
  46. package/dist/worker.js.map +1 -0
  47. package/package.json +59 -0
@@ -0,0 +1,176 @@
1
+ import { z } from 'zod';
2
+ import { withErrorHandling, formatDate, timeAgo, fmt, READ_ONLY_ANNOTATIONS, dateFilterParams, } from './helpers.js';
3
+ export function registerListeningTools(server, client) {
4
+ // get_now_playing
5
+ server.tool('get_now_playing', 'Get the track currently playing on Last.fm. Returns the song, artist, and album if something is playing.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
6
+ const data = await client.get('/listening/now-playing');
7
+ if (!data.is_playing || !data.track) {
8
+ return 'Nothing playing right now.';
9
+ }
10
+ const artist = data.artist?.name ?? 'Unknown Artist';
11
+ const album = data.album?.name ? ` (from ${data.album.name})` : '';
12
+ return `Now playing: "${data.track.name}" by ${artist}${album}`;
13
+ }));
14
+ // get_recent_listens
15
+ server.tool('get_recent_listens', 'Get recently scrobbled tracks from Last.fm. Returns a list of recent plays with artist, album, and time. Supports date filtering.', {
16
+ limit: z
17
+ .number()
18
+ .min(1)
19
+ .max(50)
20
+ .default(10)
21
+ .describe('Number of recent tracks to return (default 10, max 50)'),
22
+ ...dateFilterParams,
23
+ }, READ_ONLY_ANNOTATIONS, async ({ limit, date, from, to }) => withErrorHandling(async () => {
24
+ const data = await client.get('/listening/recent', { limit, date, from, to });
25
+ if (!data.data.length)
26
+ return 'No recent listens found.';
27
+ const lines = ['Recent listens:'];
28
+ for (const [i, s] of data.data.entries()) {
29
+ const album = s.album?.name ? ` -- ${s.album.name}` : '';
30
+ lines.push(`${i + 1}. "${s.track.name}" by ${s.artist.name}${album} (${timeAgo(s.scrobbled_at)})`);
31
+ }
32
+ return lines.join('\n');
33
+ }));
34
+ // get_listening_stats
35
+ server.tool('get_listening_stats', 'Get overall listening statistics from Last.fm including total scrobbles, unique artists, albums, tracks, and daily average. Supports date filtering for period-specific stats.', { ...dateFilterParams }, READ_ONLY_ANNOTATIONS, async ({ date, from, to }) => withErrorHandling(async () => {
36
+ const data = await client.get('/listening/stats', { date, from, to });
37
+ return [
38
+ 'Listening Stats:',
39
+ `- Total scrobbles: ${fmt(data.total_scrobbles)}`,
40
+ `- Unique artists: ${fmt(data.unique_artists)}`,
41
+ `- Unique albums: ${fmt(data.unique_albums)}`,
42
+ `- Unique tracks: ${fmt(data.unique_tracks)}`,
43
+ `- Average per day: ${data.scrobbles_per_day.toFixed(1)}`,
44
+ `- Years tracking: ${data.years_tracking}`,
45
+ ].join('\n');
46
+ }));
47
+ // get_top_artists
48
+ server.tool('get_top_artists', 'Get top listened-to artists from Last.fm for a given time period. Returns ranked list with play counts.', {
49
+ period: z
50
+ .enum(['7day', '1month', '3month', '6month', '12month', 'overall'])
51
+ .default('1month')
52
+ .describe('Time period for rankings'),
53
+ limit: z
54
+ .number()
55
+ .min(1)
56
+ .max(50)
57
+ .default(10)
58
+ .describe('Number of artists to return'),
59
+ page: z.number().min(1).default(1).describe('Page number for pagination'),
60
+ }, READ_ONLY_ANNOTATIONS, async ({ period, limit, page }) => withErrorHandling(async () => {
61
+ const data = await client.get('/listening/top/artists', { period, limit, page });
62
+ if (!data.data.length)
63
+ return `No top artists for period: ${period}`;
64
+ const lines = [`Top Artists (${period}):`];
65
+ for (const a of data.data) {
66
+ lines.push(`${a.rank}. ${a.name} -- ${fmt(a.playcount)} plays`);
67
+ }
68
+ return lines.join('\n');
69
+ }));
70
+ // get_top_albums
71
+ server.tool('get_top_albums', 'Get top listened-to albums from Last.fm for a given time period. Returns ranked list with play counts.', {
72
+ period: z
73
+ .enum(['7day', '1month', '3month', '6month', '12month', 'overall'])
74
+ .default('1month')
75
+ .describe('Time period for rankings'),
76
+ limit: z
77
+ .number()
78
+ .min(1)
79
+ .max(50)
80
+ .default(10)
81
+ .describe('Number of albums to return'),
82
+ page: z.number().min(1).default(1).describe('Page number for pagination'),
83
+ }, READ_ONLY_ANNOTATIONS, async ({ period, limit, page }) => withErrorHandling(async () => {
84
+ const data = await client.get('/listening/top/albums', { period, limit, page });
85
+ if (!data.data.length)
86
+ return `No top albums for period: ${period}`;
87
+ const lines = [`Top Albums (${period}):`];
88
+ for (const a of data.data) {
89
+ lines.push(`${a.rank}. ${a.name} by ${a.detail} -- ${fmt(a.playcount)} plays`);
90
+ }
91
+ return lines.join('\n');
92
+ }));
93
+ // get_top_tracks
94
+ server.tool('get_top_tracks', 'Get top listened-to tracks from Last.fm for a given time period. Returns ranked list with play counts.', {
95
+ period: z
96
+ .enum(['7day', '1month', '3month', '6month', '12month', 'overall'])
97
+ .default('1month')
98
+ .describe('Time period for rankings'),
99
+ limit: z
100
+ .number()
101
+ .min(1)
102
+ .max(50)
103
+ .default(10)
104
+ .describe('Number of tracks to return'),
105
+ page: z.number().min(1).default(1).describe('Page number for pagination'),
106
+ }, READ_ONLY_ANNOTATIONS, async ({ period, limit, page }) => withErrorHandling(async () => {
107
+ const data = await client.get('/listening/top/tracks', { period, limit, page });
108
+ if (!data.data.length)
109
+ return `No top tracks for period: ${period}`;
110
+ const lines = [`Top Tracks (${period}):`];
111
+ for (const t of data.data) {
112
+ lines.push(`${t.rank}. "${t.name}" by ${t.detail} -- ${fmt(t.playcount)} plays`);
113
+ }
114
+ return lines.join('\n');
115
+ }));
116
+ // get_listening_streaks
117
+ server.tool('get_listening_streaks', 'Get listening streak data from Last.fm -- current consecutive days with scrobbles and the longest streak ever.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
118
+ const data = await client.get('/listening/streaks');
119
+ return [
120
+ 'Listening Streaks:',
121
+ '',
122
+ `Current streak: ${data.current.days} days (${fmt(data.current.total_scrobbles)} scrobbles)`,
123
+ data.current.start_date
124
+ ? ` Started: ${formatDate(data.current.start_date)}`
125
+ : '',
126
+ '',
127
+ `Longest streak: ${data.longest.days} days (${fmt(data.longest.total_scrobbles)} scrobbles)`,
128
+ data.longest.start_date
129
+ ? ` ${formatDate(data.longest.start_date)} -- ${formatDate(data.longest.end_date)}`
130
+ : '',
131
+ ]
132
+ .filter(Boolean)
133
+ .join('\n');
134
+ }));
135
+ // get_artist_details
136
+ server.tool('get_artist_details', 'Get detailed information about a specific artist by ID, including play count, top albums, and top tracks.', { id: z.number().describe('Artist ID') }, READ_ONLY_ANNOTATIONS, async ({ id }) => withErrorHandling(async () => {
137
+ const data = await client.get(`/listening/artists/${id}`);
138
+ const lines = [
139
+ `${data.name}`,
140
+ `Total plays: ${fmt(data.playcount)}`,
141
+ data.genre ? `Genre: ${data.genre}` : null,
142
+ '',
143
+ ].filter((l) => l !== null);
144
+ if (data.top_albums.length) {
145
+ lines.push('Top Albums:');
146
+ for (const a of data.top_albums.slice(0, 5)) {
147
+ lines.push(` - ${a.name} (${fmt(a.playcount)} plays)`);
148
+ }
149
+ lines.push('');
150
+ }
151
+ if (data.top_tracks.length) {
152
+ lines.push('Top Tracks:');
153
+ for (const t of data.top_tracks.slice(0, 5)) {
154
+ lines.push(` - "${t.name}" (${fmt(t.scrobble_count)} plays)`);
155
+ }
156
+ }
157
+ return lines.join('\n');
158
+ }));
159
+ // get_album_details
160
+ server.tool('get_album_details', 'Get detailed information about a specific album by ID, including artist, play count, and track listing.', { id: z.number().describe('Album ID') }, READ_ONLY_ANNOTATIONS, async ({ id }) => withErrorHandling(async () => {
161
+ const data = await client.get(`/listening/albums/${id}`);
162
+ const lines = [
163
+ `${data.name} by ${data.artist.name}`,
164
+ `Total plays: ${fmt(data.playcount)}`,
165
+ '',
166
+ ];
167
+ if (data.tracks.length) {
168
+ lines.push('Tracks:');
169
+ for (const [i, t] of data.tracks.entries()) {
170
+ lines.push(` ${i + 1}. "${t.name}" (${fmt(t.scrobble_count)} plays)`);
171
+ }
172
+ }
173
+ return lines.join('\n');
174
+ }));
175
+ }
176
+ //# sourceMappingURL=listening.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listening.js","sourceRoot":"","sources":["../../src/tools/listening.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { RewindClient } from '../client.js';
3
+ export declare function registerReadingTools(server: McpServer, client: RewindClient): void;
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod';
2
+ import { withErrorHandling, formatDate, timeAgo, fmt, READ_ONLY_ANNOTATIONS, dateFilterParams, } from './helpers.js';
3
+ export function registerReadingTools(server, client) {
4
+ // get_recent_reads
5
+ server.tool('get_recent_reads', 'Get recently saved articles from Instapaper. Returns article titles, authors, domains, reading time, and status.', {
6
+ limit: z
7
+ .number()
8
+ .min(1)
9
+ .max(50)
10
+ .default(10)
11
+ .describe('Number of recent articles to return'),
12
+ ...dateFilterParams,
13
+ }, READ_ONLY_ANNOTATIONS, async ({ limit, date, from, to }) => withErrorHandling(async () => {
14
+ const { data } = await client.get('/reading/recent', { limit, date, from, to });
15
+ if (!data.length)
16
+ return 'No recent articles found.';
17
+ const lines = ['Recent reads:'];
18
+ for (const [i, a] of data.entries()) {
19
+ const author = a.author ? ` by ${a.author}` : '';
20
+ const domain = a.domain ? ` (${a.domain})` : '';
21
+ const readTime = a.estimated_read_min
22
+ ? ` -- ${a.estimated_read_min} min read`
23
+ : '';
24
+ const status = a.status === 'reading'
25
+ ? ` [${Math.round(a.progress * 100)}%]`
26
+ : a.status === 'archived'
27
+ ? ' [finished]'
28
+ : '';
29
+ lines.push(`${i + 1}. ${a.title}${author}${domain}${readTime}${status} (${timeAgo(a.saved_at)})`);
30
+ }
31
+ return lines.join('\n');
32
+ }));
33
+ // get_reading_highlights
34
+ server.tool('get_reading_highlights', 'Get saved highlights from Instapaper articles. Returns the highlighted text, optional notes, and the source article.', {
35
+ limit: z
36
+ .number()
37
+ .min(1)
38
+ .max(50)
39
+ .default(10)
40
+ .describe('Number of highlights to return'),
41
+ page: z.number().min(1).default(1).describe('Page number'),
42
+ }, READ_ONLY_ANNOTATIONS, async ({ limit, page }) => withErrorHandling(async () => {
43
+ const data = await client.get('/reading/highlights', { limit, page });
44
+ if (!data.data.length)
45
+ return 'No highlights found.';
46
+ const lines = [`Highlights (${fmt(data.pagination.total)} total):`];
47
+ for (const h of data.data) {
48
+ const source = h.article.author
49
+ ? `${h.article.title} by ${h.article.author}`
50
+ : h.article.title;
51
+ lines.push('');
52
+ lines.push(`"${h.text}"`);
53
+ if (h.note)
54
+ lines.push(` Note: ${h.note}`);
55
+ lines.push(` -- ${source} (${formatDate(h.created_at)})`);
56
+ }
57
+ return lines.join('\n');
58
+ }));
59
+ // get_random_highlight
60
+ server.tool('get_random_highlight', 'Get a single random highlight from saved Instapaper articles. Great for daily inspiration or reflection.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
61
+ const data = await client.get('/reading/highlights/random');
62
+ const source = data.article.author
63
+ ? `${data.article.title} by ${data.article.author}`
64
+ : data.article.title;
65
+ const lines = [`"${data.text}"`];
66
+ if (data.note)
67
+ lines.push(`Note: ${data.note}`);
68
+ lines.push(`-- ${source}`);
69
+ return lines.join('\n');
70
+ }));
71
+ // get_reading_stats
72
+ server.tool('get_reading_stats', 'Get overall reading statistics from Instapaper including total articles, finished count, currently reading, highlights, and word count.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
73
+ const data = await client.get('/reading/stats');
74
+ return [
75
+ 'Reading Stats:',
76
+ `- Total articles: ${fmt(data.total_articles)}`,
77
+ `- Finished: ${fmt(data.finished_count)}`,
78
+ `- Currently reading: ${data.currently_reading_count}`,
79
+ `- Total highlights: ${fmt(data.total_highlights)}`,
80
+ `- Total words read: ${fmt(data.total_word_count)}`,
81
+ `- Average read time: ${Math.round(data.avg_estimated_read_min)} min`,
82
+ ].join('\n');
83
+ }));
84
+ }
85
+ //# sourceMappingURL=reading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reading.js","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { RewindClient } from '../client.js';
3
+ export declare function registerRunningTools(server: McpServer, client: RewindClient): void;
@@ -0,0 +1,135 @@
1
+ import { z } from 'zod';
2
+ import { withErrorHandling, formatDate, timeAgo, fmt, READ_ONLY_ANNOTATIONS, } from './helpers.js';
3
+ export function registerRunningTools(server, client) {
4
+ // get_running_stats
5
+ server.tool('get_running_stats', 'Get overall running statistics from Strava including total runs, distance, elevation, duration, average pace, and Eddington number.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
6
+ const { data } = await client.get('/running/stats');
7
+ return [
8
+ 'Running Stats:',
9
+ `- Total runs: ${fmt(data.total_runs)}`,
10
+ `- Total distance: ${fmt(Math.round(data.total_distance_mi))} mi`,
11
+ `- Total elevation: ${fmt(Math.round(data.total_elevation_ft))} ft`,
12
+ `- Total time: ${data.total_duration}`,
13
+ data.avg_pace ? `- Average pace: ${data.avg_pace}` : null,
14
+ `- Years active: ${data.years_active}`,
15
+ data.first_run
16
+ ? `- Running since: ${formatDate(data.first_run)}`
17
+ : null,
18
+ `- Eddington number: ${data.eddington_number}`,
19
+ ]
20
+ .filter((l) => l !== null)
21
+ .join('\n');
22
+ }));
23
+ // get_recent_runs
24
+ server.tool('get_recent_runs', 'Get recent running activities from Strava. Returns a list of runs with ID, distance, pace, duration, and location. Use the ID with get_activity_details or get_activity_splits for more info.', {
25
+ limit: z
26
+ .number()
27
+ .min(1)
28
+ .max(50)
29
+ .default(10)
30
+ .describe('Number of recent runs to return'),
31
+ date: z
32
+ .string()
33
+ .optional()
34
+ .describe('Optional: filter to a specific date (YYYY-MM-DD)'),
35
+ from: z
36
+ .string()
37
+ .optional()
38
+ .describe('Optional: start of date range (ISO 8601)'),
39
+ to: z
40
+ .string()
41
+ .optional()
42
+ .describe('Optional: end of date range (ISO 8601)'),
43
+ }, READ_ONLY_ANNOTATIONS, async ({ limit, date, from, to }) => withErrorHandling(async () => {
44
+ const { data } = await client.get('/running/recent', { limit, date, from, to });
45
+ if (!data.length)
46
+ return 'No recent runs found.';
47
+ const lines = ['Recent runs:'];
48
+ for (const [i, r] of data.entries()) {
49
+ const location = r.city && r.state ? ` in ${r.city}, ${r.state}` : '';
50
+ const race = r.is_race ? ' [RACE]' : '';
51
+ lines.push(`${i + 1}. [ID: ${r.id}] ${r.name}${race} -- ${r.distance_mi.toFixed(1)} mi, ${r.pace}/mi, ${r.duration}${location} (${timeAgo(r.date)})`);
52
+ }
53
+ return lines.join('\n');
54
+ }));
55
+ // get_personal_records
56
+ server.tool('get_personal_records', 'Get personal running records (PRs) from Strava -- fastest times at standard distances like mile, 5K, 10K, half marathon, marathon.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
57
+ const { data } = await client.get('/running/prs');
58
+ if (!data.length)
59
+ return 'No personal records found.';
60
+ const lines = ['Personal Records:'];
61
+ for (const pr of data) {
62
+ lines.push(`- ${pr.distance_label}: ${pr.time} (${pr.pace}/mi) -- ${pr.activity_name} [ID: ${pr.activity_id}], ${formatDate(pr.date)}`);
63
+ }
64
+ return lines.join('\n');
65
+ }));
66
+ // get_running_streaks
67
+ server.tool('get_running_streaks', 'Get running streak data from Strava -- current consecutive days with runs and the longest streak ever.', {}, READ_ONLY_ANNOTATIONS, async () => withErrorHandling(async () => {
68
+ const { data } = await client.get('/running/streaks');
69
+ return [
70
+ 'Running Streaks:',
71
+ '',
72
+ `Current streak: ${data.current.days} days`,
73
+ data.current.start
74
+ ? ` ${formatDate(data.current.start)} -- ${formatDate(data.current.end)}`
75
+ : '',
76
+ '',
77
+ `Longest streak: ${data.longest.days} days`,
78
+ data.longest.start
79
+ ? ` ${formatDate(data.longest.start)} -- ${formatDate(data.longest.end)}`
80
+ : '',
81
+ ]
82
+ .filter(Boolean)
83
+ .join('\n');
84
+ }));
85
+ // get_activity_details
86
+ server.tool('get_activity_details', 'Get detailed information about a specific running activity by ID, including distance, pace, heart rate, elevation, and calories. Get the ID from get_recent_runs.', {
87
+ id: z
88
+ .number()
89
+ .describe('Activity ID (from get_recent_runs or get_personal_records)'),
90
+ }, READ_ONLY_ANNOTATIONS, async ({ id }) => withErrorHandling(async () => {
91
+ const data = await client.get(`/running/activities/${id}`);
92
+ const lines = [
93
+ `${data.name}${data.is_race ? ' [RACE]' : ''}`,
94
+ `Date: ${formatDate(data.date)}`,
95
+ `Distance: ${data.distance_mi.toFixed(2)} mi`,
96
+ `Duration: ${data.duration}`,
97
+ `Pace: ${data.pace}/mi`,
98
+ `Elevation: ${fmt(Math.round(data.elevation_ft))} ft`,
99
+ `Type: ${data.workout_type}`,
100
+ ];
101
+ if (data.city && data.state) {
102
+ lines.push(`Location: ${data.city}, ${data.state}`);
103
+ }
104
+ if (data.heartrate_avg) {
105
+ lines.push(`Heart rate: ${Math.round(data.heartrate_avg)} avg / ${data.heartrate_max ?? '?'} max bpm`);
106
+ }
107
+ if (data.cadence)
108
+ lines.push(`Cadence: ${Math.round(data.cadence)} spm`);
109
+ if (data.calories)
110
+ lines.push(`Calories: ${fmt(data.calories)}`);
111
+ if (data.strava_url)
112
+ lines.push(`Strava: ${data.strava_url}`);
113
+ return lines.join('\n');
114
+ }));
115
+ // get_activity_splits
116
+ server.tool('get_activity_splits', 'Get per-mile splits for a running activity. Shows pace, elevation, and heart rate for each mile. Get the activity ID from get_recent_runs.', {
117
+ id: z
118
+ .number()
119
+ .describe('Activity ID (from get_recent_runs or get_personal_records)'),
120
+ }, READ_ONLY_ANNOTATIONS, async ({ id }) => withErrorHandling(async () => {
121
+ const { data } = await client.get(`/running/activities/${id}/splits`);
122
+ if (!data.length)
123
+ return 'No splits found for this activity.';
124
+ const lines = [`Mile splits for activity ${id}:`];
125
+ for (const s of data) {
126
+ const hr = s.heartrate ? ` | ${Math.round(s.heartrate)} bpm` : '';
127
+ const elev = s.elevation_ft !== 0
128
+ ? ` | ${s.elevation_ft > 0 ? '+' : ''}${Math.round(s.elevation_ft)} ft`
129
+ : '';
130
+ lines.push(` Mile ${s.split}: ${s.pace}${elev}${hr}`);
131
+ }
132
+ return lines.join('\n');
133
+ }));
134
+ }
135
+ //# sourceMappingURL=running.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"running.js","sourceRoot":"","sources":["../../src/tools/running.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { RewindClient } from '../client.js';
3
+ export declare function registerWatchingTools(server: McpServer, client: RewindClient): void;
@@ -0,0 +1,138 @@
1
+ import { z } from 'zod';
2
+ import { withErrorHandling, formatDate, timeAgo, fmt, READ_ONLY_ANNOTATIONS, dateFilterParams, } from './helpers.js';
3
+ export function registerWatchingTools(server, client) {
4
+ // get_recent_watches
5
+ server.tool('get_recent_watches', 'Get recently watched movies and TV shows from Plex and Letterboxd. Returns titles, ratings, and watch dates.', {
6
+ limit: z
7
+ .number()
8
+ .min(1)
9
+ .max(50)
10
+ .default(10)
11
+ .describe('Number of recent watches to return'),
12
+ ...dateFilterParams,
13
+ }, READ_ONLY_ANNOTATIONS, async ({ limit, date, from, to }) => withErrorHandling(async () => {
14
+ const { data } = await client.get('/watching/recent', { limit, date, from, to });
15
+ if (!data.length)
16
+ return 'No recent watches found.';
17
+ const lines = ['Recent watches:'];
18
+ for (const [i, w] of data.entries()) {
19
+ const year = w.movie.year ? ` (${w.movie.year})` : '';
20
+ const director = w.movie.director ? ` dir. ${w.movie.director}` : '';
21
+ const rating = w.user_rating !== null ? ` -- ${w.user_rating}/10` : '';
22
+ const rewatch = w.rewatch ? ' [rewatch]' : '';
23
+ lines.push(`${i + 1}. ${w.movie.title}${year}${director}${rating}${rewatch} (${timeAgo(w.watched_at)})`);
24
+ }
25
+ return lines.join('\n');
26
+ }));
27
+ // get_movie_details
28
+ server.tool('get_movie_details', 'Get detailed information about a specific movie by ID, including director, genres, rating, summary, and watch history.', { id: z.number().describe('Movie ID') }, READ_ONLY_ANNOTATIONS, async ({ id }) => withErrorHandling(async () => {
29
+ const data = await client.get(`/watching/movies/${id}`);
30
+ const lines = [
31
+ `${data.title}${data.year ? ` (${data.year})` : ''}`,
32
+ data.tagline ? `"${data.tagline}"` : null,
33
+ '',
34
+ data.directors.length
35
+ ? `Director${data.directors.length > 1 ? 's' : ''}: ${data.directors.join(', ')}`
36
+ : null,
37
+ data.genres.length ? `Genres: ${data.genres.join(', ')}` : null,
38
+ data.duration_min ? `Runtime: ${data.duration_min} min` : null,
39
+ data.rating ? `Rated: ${data.rating}` : null,
40
+ data.tmdb_rating ? `TMDB: ${data.tmdb_rating}/10` : null,
41
+ data.summary ? `\n${data.summary}` : null,
42
+ ].filter((l) => l !== null);
43
+ if (data.watch_history.length) {
44
+ lines.push('', 'Watch History:');
45
+ for (const w of data.watch_history) {
46
+ const rating = w.user_rating !== null ? ` -- rated ${w.user_rating}/10` : '';
47
+ const rewatch = w.rewatch ? ' [rewatch]' : '';
48
+ const source = w.source ? ` via ${w.source}` : '';
49
+ lines.push(` - ${formatDate(w.watched_at)}${rating}${rewatch}${source}`);
50
+ if (w.review) {
51
+ lines.push(` Review: ${w.review.slice(0, 200)}${w.review.length > 200 ? '...' : ''}`);
52
+ }
53
+ }
54
+ }
55
+ return lines.join('\n');
56
+ }));
57
+ // get_watching_stats
58
+ server.tool('get_watching_stats', 'Get overall watching statistics including total movies, watch time, movies this year, top genre, top director, TV show counts, and episode counts. Supports date filtering.', { ...dateFilterParams }, READ_ONLY_ANNOTATIONS, async ({ date, from, to }) => withErrorHandling(async () => {
59
+ const { data } = await client.get('/watching/stats', { date, from, to });
60
+ return [
61
+ 'Watching Stats:',
62
+ '',
63
+ 'Movies:',
64
+ `- Total movies: ${fmt(data.total_movies)}`,
65
+ `- Total watch time: ${fmt(Math.round(data.total_watch_time_hours))} hours`,
66
+ `- Movies this year: ${data.movies_this_year}`,
67
+ `- Average per month: ${data.avg_per_month.toFixed(1)}`,
68
+ data.top_genre ? `- Top genre: ${data.top_genre}` : null,
69
+ data.top_director ? `- Top director: ${data.top_director}` : null,
70
+ data.top_decade ? `- Top decade: ${data.top_decade}s` : null,
71
+ '',
72
+ 'TV Shows:',
73
+ `- Total shows: ${fmt(data.total_shows)}`,
74
+ `- Total episodes: ${fmt(data.total_episodes_watched)}`,
75
+ `- Episodes this year: ${data.episodes_this_year}`,
76
+ ]
77
+ .filter((l) => l !== null)
78
+ .join('\n');
79
+ }));
80
+ // browse_movies
81
+ server.tool('browse_movies', 'Browse the movie collection with filters for genre, decade, director, and year. Supports sorting and pagination.', {
82
+ genre: z
83
+ .string()
84
+ .optional()
85
+ .describe("Optional: filter by genre (e.g. 'Horror', 'Comedy', 'Drama')"),
86
+ decade: z
87
+ .string()
88
+ .optional()
89
+ .describe("Optional: filter by decade (e.g. '1990', '2000')"),
90
+ director: z
91
+ .string()
92
+ .optional()
93
+ .describe('Optional: filter by director name'),
94
+ year: z.string().optional().describe('Optional: filter by release year'),
95
+ sort: z
96
+ .string()
97
+ .optional()
98
+ .describe('Optional: sort by field (default: watched_at). Options: watched_at, title, year, rating'),
99
+ order: z
100
+ .enum(['asc', 'desc'])
101
+ .optional()
102
+ .describe('Optional: sort order (default: desc)'),
103
+ limit: z
104
+ .number()
105
+ .min(1)
106
+ .max(50)
107
+ .default(10)
108
+ .describe('Number of movies to return'),
109
+ page: z.number().min(1).default(1).describe('Page number'),
110
+ }, READ_ONLY_ANNOTATIONS, async ({ genre, decade, director, year, sort, order, limit, page }) => withErrorHandling(async () => {
111
+ const data = await client.get('/watching/movies', {
112
+ genre,
113
+ decade,
114
+ director,
115
+ year,
116
+ sort,
117
+ order,
118
+ limit,
119
+ page,
120
+ });
121
+ if (!data.data.length)
122
+ return 'No movies found matching those filters.';
123
+ const filters = [genre, decade, director, year].filter(Boolean);
124
+ const header = filters.length
125
+ ? `Movies (filtered: ${filters.join(', ')})`
126
+ : `Movies (page ${data.pagination.page} of ${data.pagination.total_pages})`;
127
+ const lines = [`${header} -- ${fmt(data.pagination.total)} total:`];
128
+ for (const [i, m] of data.data.entries()) {
129
+ const num = (data.pagination.page - 1) * data.pagination.limit + i + 1;
130
+ const yr = m.year ? ` (${m.year})` : '';
131
+ const dir = m.director ? ` dir. ${m.director}` : '';
132
+ const rating = m.tmdb_rating ? ` -- ${m.tmdb_rating}/10` : '';
133
+ lines.push(`${num}. [ID: ${m.id}] ${m.title}${yr}${dir}${rating}`);
134
+ }
135
+ return lines.join('\n');
136
+ }));
137
+ }
138
+ //# sourceMappingURL=watching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watching.js","sourceRoot":"","sources":["../../src/tools/watching.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Cloudflare Worker entry point for remote MCP access (Claude iOS, claude.ai).
3
+ * Deployed at mcp.rewind.rest.
4
+ *
5
+ * Uses @cloudflare/workers-oauth-provider for OAuth 2.1 with GitHub as the
6
+ * upstream identity provider. The OAuth layer is only for the remote transport;
7
+ * local stdio transport (Claude Desktop, Claude Code) uses REWIND_API_KEY directly.
8
+ */
9
+ import { OAuthProvider } from '@cloudflare/workers-oauth-provider';
10
+ interface Env {
11
+ OAUTH_KV: KVNamespace;
12
+ REWIND_API_URL: string;
13
+ REWIND_API_KEY: string;
14
+ GITHUB_CLIENT_ID: string;
15
+ GITHUB_CLIENT_SECRET: string;
16
+ /** JSON map of GitHub user ID -> Rewind user_id, e.g. {"12345": 1} */
17
+ USER_ALLOWLIST: string;
18
+ }
19
+ declare const provider: OAuthProvider<Env>;
20
+ export default provider;