appstore-mcp 1.0.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 (61) hide show
  1. package/.claude/CLAUDE.md +29 -0
  2. package/.claude/settings.local.json +24 -0
  3. package/.prettierrc +7 -0
  4. package/README.md +170 -0
  5. package/dist/app-store-service.d.ts +21 -0
  6. package/dist/app-store-service.d.ts.map +1 -0
  7. package/dist/app-store-service.js +146 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +22 -0
  11. package/dist/mcp/resources/countries.d.ts +5 -0
  12. package/dist/mcp/resources/countries.d.ts.map +1 -0
  13. package/dist/mcp/resources/countries.js +236 -0
  14. package/dist/mcp/resources/index.d.ts +3 -0
  15. package/dist/mcp/resources/index.d.ts.map +1 -0
  16. package/dist/mcp/resources/index.js +80 -0
  17. package/dist/mcp/tools/get-app-details.d.ts +3 -0
  18. package/dist/mcp/tools/get-app-details.d.ts.map +1 -0
  19. package/dist/mcp/tools/get-app-details.js +84 -0
  20. package/dist/mcp/tools/get-app-info.d.ts +3 -0
  21. package/dist/mcp/tools/get-app-info.d.ts.map +1 -0
  22. package/dist/mcp/tools/get-app-info.js +156 -0
  23. package/dist/mcp/tools/get-app-release-info.d.ts +3 -0
  24. package/dist/mcp/tools/get-app-release-info.d.ts.map +1 -0
  25. package/dist/mcp/tools/get-app-release-info.js +80 -0
  26. package/dist/mcp/tools/get-app-screenshots.d.ts +3 -0
  27. package/dist/mcp/tools/get-app-screenshots.d.ts.map +1 -0
  28. package/dist/mcp/tools/get-app-screenshots.js +71 -0
  29. package/dist/mcp/tools/get-trending-apps.d.ts +3 -0
  30. package/dist/mcp/tools/get-trending-apps.d.ts.map +1 -0
  31. package/dist/mcp/tools/get-trending-apps.js +89 -0
  32. package/dist/mcp/tools/index.d.ts +3 -0
  33. package/dist/mcp/tools/index.d.ts.map +1 -0
  34. package/dist/mcp/tools/index.js +12 -0
  35. package/dist/mcp/tools/list-categories.d.ts +3 -0
  36. package/dist/mcp/tools/list-categories.d.ts.map +1 -0
  37. package/dist/mcp/tools/list-categories.js +38 -0
  38. package/dist/mcp/tools/list-countries.d.ts +3 -0
  39. package/dist/mcp/tools/list-countries.d.ts.map +1 -0
  40. package/dist/mcp/tools/list-countries.js +40 -0
  41. package/dist/mcp/tools/search-apps.d.ts +3 -0
  42. package/dist/mcp/tools/search-apps.d.ts.map +1 -0
  43. package/dist/mcp/tools/search-apps.js +67 -0
  44. package/dist/services/app-store-service.d.ts +21 -0
  45. package/dist/services/app-store-service.d.ts.map +1 -0
  46. package/dist/services/app-store-service.js +146 -0
  47. package/dist/types.d.ts +46 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +41 -0
  50. package/eslint.config.js +9 -0
  51. package/package.json +46 -0
  52. package/src/index.ts +24 -0
  53. package/src/mcp/resources/countries.ts +234 -0
  54. package/src/mcp/resources/index.ts +117 -0
  55. package/src/mcp/tools/get-app-info.ts +183 -0
  56. package/src/mcp/tools/get-trending-apps.ts +108 -0
  57. package/src/mcp/tools/index.ts +10 -0
  58. package/src/mcp/tools/search-apps.ts +74 -0
  59. package/src/services/app-store-service.ts +236 -0
  60. package/src/types.ts +77 -0
  61. package/tsconfig.json +21 -0
@@ -0,0 +1,236 @@
1
+ import axios from 'axios';
2
+ import { App, Platform, PricingType } from '../types';
3
+
4
+ const ITUNES_SEARCH_URL = 'https://itunes.apple.com/search';
5
+ const ITUNES_LOOKUP_URL = 'https://itunes.apple.com/lookup';
6
+ const ITUNES_RSS_BASE = 'https://itunes.apple.com';
7
+
8
+ interface SearchResponse {
9
+ resultCount: number;
10
+ results: ItunesSearchResult[];
11
+ }
12
+
13
+ interface ItunesSearchResult {
14
+ trackId: number;
15
+ trackName: string;
16
+ trackViewUrl: string;
17
+ artworkUrl100: string;
18
+ artistName: string;
19
+ averageUserRating?: number;
20
+ userRatingCount?: number;
21
+ releaseDate?: string;
22
+ currentVersionReleaseDate?: string;
23
+ description?: string;
24
+ version?: string;
25
+ price?: number;
26
+ formattedPrice?: string;
27
+ primaryGenreName?: string;
28
+ genres?: string[];
29
+ screenshotUrls?: string[];
30
+ ipadScreenshotUrls?: string[];
31
+ bundleId?: string;
32
+ minimumOsVersion?: string;
33
+ fileSizeBytes?: string;
34
+ contentAdvisoryRating?: string;
35
+ }
36
+
37
+ interface RssEntry {
38
+ 'im:name': { label: string };
39
+ 'im:image': { label: string }[];
40
+ id: {
41
+ label: string;
42
+ attributes: { 'im:id': string };
43
+ };
44
+ 'im:artist': { label: string };
45
+ 'im:releaseDate'?: { label: string };
46
+ summary?: { label: string };
47
+ 'im:price'?: { label: string };
48
+ category?: {
49
+ attributes: { label: string };
50
+ };
51
+ }
52
+
53
+ interface RssResponse {
54
+ feed: {
55
+ entry: RssEntry[];
56
+ };
57
+ }
58
+
59
+ export class AppStoreService {
60
+ private static getFeedName(platform: Platform, pricingType: PricingType): string {
61
+ const feedMap: Record<Platform, Record<PricingType, string>> = {
62
+ iphone: {
63
+ free: 'topfreeapplications',
64
+ paid: 'toppaidapplications',
65
+ grossing: 'topgrossingapplications',
66
+ },
67
+ ipad: {
68
+ free: 'topfreeipadapplications',
69
+ paid: 'toppaidipadapplications',
70
+ grossing: 'topgrossingipadapplications',
71
+ },
72
+ mac: {
73
+ free: 'topfreemacapps',
74
+ paid: 'toppaidmacapps',
75
+ grossing: 'topgrossingmacapps',
76
+ },
77
+ tv: {
78
+ free: 'topfreeappletvapps',
79
+ paid: 'toppaidappletvapps',
80
+ grossing: 'topgrossingappletvapps',
81
+ },
82
+ };
83
+
84
+ return feedMap[platform][pricingType];
85
+ }
86
+
87
+ private static buildRssUrl(
88
+ countryCode: string,
89
+ feedName: string,
90
+ limit: number,
91
+ categoryId?: string
92
+ ): string {
93
+ const genrePart = categoryId ? `/genre=${categoryId}` : '';
94
+ return `${ITUNES_RSS_BASE}/${countryCode}/rss/${feedName}/limit=${limit}${genrePart}/json`;
95
+ }
96
+
97
+ private static parseSearchResult(item: ItunesSearchResult): App {
98
+ return {
99
+ id: String(item.trackId),
100
+ name: item.trackName,
101
+ url: item.trackViewUrl,
102
+ iconUrl: item.artworkUrl100,
103
+ artistName: item.artistName,
104
+ averageUserRating: item.averageUserRating,
105
+ userRatingCount: item.userRatingCount,
106
+ releaseDate: item.releaseDate,
107
+ currentVersionReleaseDate: item.currentVersionReleaseDate,
108
+ description: item.description,
109
+ version: item.version,
110
+ price: item.price,
111
+ formattedPrice: item.formattedPrice,
112
+ primaryGenreName: item.primaryGenreName,
113
+ genres: item.genres,
114
+ screenshotUrls: item.screenshotUrls,
115
+ ipadScreenshotUrls: item.ipadScreenshotUrls,
116
+ bundleId: item.bundleId,
117
+ minimumOsVersion: item.minimumOsVersion,
118
+ fileSizeBytes: item.fileSizeBytes,
119
+ contentAdvisoryRating: item.contentAdvisoryRating,
120
+ };
121
+ }
122
+
123
+ private static parseRssEntry(entry: RssEntry): App {
124
+ const images = entry['im:image'] || [];
125
+ const largestImage = images.length > 0 ? images[images.length - 1].label : '';
126
+
127
+ return {
128
+ id: entry.id.attributes['im:id'],
129
+ name: entry['im:name'].label,
130
+ url: entry.id.label,
131
+ iconUrl: largestImage,
132
+ artistName: entry['im:artist'].label,
133
+ releaseDate: entry['im:releaseDate']?.label,
134
+ description: entry.summary?.label,
135
+ formattedPrice: entry['im:price']?.label,
136
+ primaryGenreName: entry.category?.attributes.label,
137
+ };
138
+ }
139
+
140
+ static async searchApps(query: string, country: string = 'US', limit: number = 25): Promise<App[]> {
141
+ const params = {
142
+ term: query,
143
+ country: country,
144
+ media: 'software',
145
+ entity: 'software',
146
+ limit: limit,
147
+ };
148
+
149
+ const response = await axios.get<SearchResponse>(ITUNES_SEARCH_URL, { params });
150
+ return response.data.results.map(this.parseSearchResult);
151
+ }
152
+
153
+ static async getAppDetails(appId: string, country: string = 'US'): Promise<App | null> {
154
+ const params = {
155
+ id: appId,
156
+ country: country,
157
+ };
158
+
159
+ const response = await axios.get<SearchResponse>(ITUNES_LOOKUP_URL, { params });
160
+
161
+ if (response.data.resultCount === 0) {
162
+ return null;
163
+ }
164
+
165
+ return this.parseSearchResult(response.data.results[0]);
166
+ }
167
+
168
+ static async lookupMultipleApps(
169
+ appIds: string[],
170
+ country: string = 'US'
171
+ ): Promise<Map<string, App>> {
172
+ const params = {
173
+ id: appIds.join(','),
174
+ country: country,
175
+ };
176
+
177
+ const response = await axios.get<SearchResponse>(ITUNES_LOOKUP_URL, { params });
178
+ const appMap = new Map<string, App>();
179
+
180
+ for (const result of response.data.results) {
181
+ const app = this.parseSearchResult(result);
182
+ appMap.set(app.id, app);
183
+ }
184
+
185
+ return appMap;
186
+ }
187
+
188
+ static async getTrendingApps(
189
+ platform: Platform = 'iphone',
190
+ pricingType: PricingType = 'free',
191
+ country: string = 'US',
192
+ categoryId?: string,
193
+ limit: number = 25
194
+ ): Promise<App[]> {
195
+ const feedName = this.getFeedName(platform, pricingType);
196
+ const url = this.buildRssUrl(country, feedName, limit, categoryId);
197
+
198
+ const response = await axios.get<RssResponse>(url);
199
+ const entries = response.data.feed.entry || [];
200
+
201
+ return entries.map(this.parseRssEntry);
202
+ }
203
+
204
+ static async getAppReleaseInfo(
205
+ appId: string,
206
+ country: string = 'US'
207
+ ): Promise<{ releaseDate?: string; currentVersionReleaseDate?: string; version?: string } | null> {
208
+ const app = await this.getAppDetails(appId, country);
209
+
210
+ if (!app) {
211
+ return null;
212
+ }
213
+
214
+ return {
215
+ releaseDate: app.releaseDate,
216
+ currentVersionReleaseDate: app.currentVersionReleaseDate,
217
+ version: app.version,
218
+ };
219
+ }
220
+
221
+ static async getAppScreenshots(
222
+ appId: string,
223
+ country: string = 'US'
224
+ ): Promise<{ screenshotUrls: string[]; ipadScreenshotUrls: string[] } | null> {
225
+ const app = await this.getAppDetails(appId, country);
226
+
227
+ if (!app) {
228
+ return null;
229
+ }
230
+
231
+ return {
232
+ screenshotUrls: app.screenshotUrls || [],
233
+ ipadScreenshotUrls: app.ipadScreenshotUrls || [],
234
+ };
235
+ }
236
+ }
package/src/types.ts ADDED
@@ -0,0 +1,77 @@
1
+ export interface App {
2
+ id: string;
3
+ name: string;
4
+ url: string;
5
+ iconUrl: string;
6
+ artistName: string;
7
+ averageUserRating?: number;
8
+ userRatingCount?: number;
9
+ releaseDate?: string;
10
+ currentVersionReleaseDate?: string;
11
+ description?: string;
12
+ version?: string;
13
+ price?: number;
14
+ formattedPrice?: string;
15
+ primaryGenreName?: string;
16
+ genres?: string[];
17
+ screenshotUrls?: string[];
18
+ ipadScreenshotUrls?: string[];
19
+ bundleId?: string;
20
+ minimumOsVersion?: string;
21
+ fileSizeBytes?: string;
22
+ contentAdvisoryRating?: string;
23
+ }
24
+
25
+ export interface Category {
26
+ id: string;
27
+ name: string;
28
+ emoji: string;
29
+ }
30
+
31
+ export type Platform = 'iphone' | 'ipad' | 'mac' | 'tv';
32
+ export type PricingType = 'free' | 'paid' | 'grossing';
33
+
34
+ export interface Country {
35
+ code: string;
36
+ name: string;
37
+ }
38
+
39
+ export const APP_STORE_CATEGORIES: Category[] = [
40
+ { id: '6000', name: 'Business', emoji: '💼' },
41
+ { id: '6001', name: 'Weather', emoji: '🌤️' },
42
+ { id: '6002', name: 'Utilities', emoji: '🔧' },
43
+ { id: '6003', name: 'Travel', emoji: '✈️' },
44
+ { id: '6004', name: 'Sports', emoji: '⚽' },
45
+ { id: '6005', name: 'Social Networking', emoji: '👥' },
46
+ { id: '6006', name: 'Reference', emoji: '📚' },
47
+ { id: '6007', name: 'Productivity', emoji: '📊' },
48
+ { id: '6008', name: 'Photo & Video', emoji: '📷' },
49
+ { id: '6009', name: 'News', emoji: '📰' },
50
+ { id: '6010', name: 'Navigation', emoji: '🧭' },
51
+ { id: '6011', name: 'Music', emoji: '🎵' },
52
+ { id: '6012', name: 'Lifestyle', emoji: '🌟' },
53
+ { id: '6013', name: 'Health & Fitness', emoji: '💪' },
54
+ { id: '6014', name: 'Games', emoji: '🎮' },
55
+ { id: '6015', name: 'Finance', emoji: '💰' },
56
+ { id: '6016', name: 'Entertainment', emoji: '🎬' },
57
+ { id: '6017', name: 'Education', emoji: '🎓' },
58
+ { id: '6018', name: 'Books', emoji: '📖' },
59
+ { id: '6020', name: 'Medical', emoji: '🏥' },
60
+ { id: '6021', name: 'Magazines & Newspapers', emoji: '📰' },
61
+ { id: '6022', name: 'Catalogs', emoji: '📋' },
62
+ { id: '6023', name: 'Food & Drink', emoji: '🍔' },
63
+ { id: '6024', name: 'Shopping', emoji: '🛒' },
64
+ ];
65
+
66
+ export const PLATFORMS: { value: Platform; label: string; emoji: string }[] = [
67
+ { value: 'iphone', label: 'iPhone', emoji: '📱' },
68
+ { value: 'ipad', label: 'iPad', emoji: '📲' },
69
+ { value: 'mac', label: 'Mac', emoji: '💻' },
70
+ { value: 'tv', label: 'Apple TV', emoji: '📺' },
71
+ ];
72
+
73
+ export const PRICING_TYPES: { value: PricingType; label: string; emoji: string }[] = [
74
+ { value: 'free', label: 'Free', emoji: '🆓' },
75
+ { value: 'paid', label: 'Paid', emoji: '💵' },
76
+ { value: 'grossing', label: 'Top Grossing', emoji: '💰' },
77
+ ];
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "lib": ["ES2022"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "inlineSourceMap": true,
16
+ "noUnusedLocals": false,
17
+ "noUnusedParameters": false
18
+ },
19
+ "include": ["src/**/*"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }