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.
- package/.claude/CLAUDE.md +29 -0
- package/.claude/settings.local.json +24 -0
- package/.prettierrc +7 -0
- package/README.md +170 -0
- package/dist/app-store-service.d.ts +21 -0
- package/dist/app-store-service.d.ts.map +1 -0
- package/dist/app-store-service.js +146 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/mcp/resources/countries.d.ts +5 -0
- package/dist/mcp/resources/countries.d.ts.map +1 -0
- package/dist/mcp/resources/countries.js +236 -0
- package/dist/mcp/resources/index.d.ts +3 -0
- package/dist/mcp/resources/index.d.ts.map +1 -0
- package/dist/mcp/resources/index.js +80 -0
- package/dist/mcp/tools/get-app-details.d.ts +3 -0
- package/dist/mcp/tools/get-app-details.d.ts.map +1 -0
- package/dist/mcp/tools/get-app-details.js +84 -0
- package/dist/mcp/tools/get-app-info.d.ts +3 -0
- package/dist/mcp/tools/get-app-info.d.ts.map +1 -0
- package/dist/mcp/tools/get-app-info.js +156 -0
- package/dist/mcp/tools/get-app-release-info.d.ts +3 -0
- package/dist/mcp/tools/get-app-release-info.d.ts.map +1 -0
- package/dist/mcp/tools/get-app-release-info.js +80 -0
- package/dist/mcp/tools/get-app-screenshots.d.ts +3 -0
- package/dist/mcp/tools/get-app-screenshots.d.ts.map +1 -0
- package/dist/mcp/tools/get-app-screenshots.js +71 -0
- package/dist/mcp/tools/get-trending-apps.d.ts +3 -0
- package/dist/mcp/tools/get-trending-apps.d.ts.map +1 -0
- package/dist/mcp/tools/get-trending-apps.js +89 -0
- package/dist/mcp/tools/index.d.ts +3 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +12 -0
- package/dist/mcp/tools/list-categories.d.ts +3 -0
- package/dist/mcp/tools/list-categories.d.ts.map +1 -0
- package/dist/mcp/tools/list-categories.js +38 -0
- package/dist/mcp/tools/list-countries.d.ts +3 -0
- package/dist/mcp/tools/list-countries.d.ts.map +1 -0
- package/dist/mcp/tools/list-countries.js +40 -0
- package/dist/mcp/tools/search-apps.d.ts +3 -0
- package/dist/mcp/tools/search-apps.d.ts.map +1 -0
- package/dist/mcp/tools/search-apps.js +67 -0
- package/dist/services/app-store-service.d.ts +21 -0
- package/dist/services/app-store-service.d.ts.map +1 -0
- package/dist/services/app-store-service.js +146 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +41 -0
- package/eslint.config.js +9 -0
- package/package.json +46 -0
- package/src/index.ts +24 -0
- package/src/mcp/resources/countries.ts +234 -0
- package/src/mcp/resources/index.ts +117 -0
- package/src/mcp/tools/get-app-info.ts +183 -0
- package/src/mcp/tools/get-trending-apps.ts +108 -0
- package/src/mcp/tools/index.ts +10 -0
- package/src/mcp/tools/search-apps.ts +74 -0
- package/src/services/app-store-service.ts +236 -0
- package/src/types.ts +77 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { COUNTRIES } from './countries';
|
|
3
|
+
import { APP_STORE_CATEGORIES, PLATFORMS, PRICING_TYPES } from '../../types';
|
|
4
|
+
|
|
5
|
+
export function registerAllResources(server: McpServer) {
|
|
6
|
+
server.registerResource(
|
|
7
|
+
'countries',
|
|
8
|
+
'appstore://countries',
|
|
9
|
+
{
|
|
10
|
+
description: 'List of all available App Store country codes',
|
|
11
|
+
mimeType: 'application/json',
|
|
12
|
+
},
|
|
13
|
+
async (uri) => ({
|
|
14
|
+
contents: [
|
|
15
|
+
{
|
|
16
|
+
uri: uri.href,
|
|
17
|
+
mimeType: 'application/json',
|
|
18
|
+
text: JSON.stringify(
|
|
19
|
+
{
|
|
20
|
+
count: COUNTRIES.length,
|
|
21
|
+
countries: COUNTRIES.map((country) => ({
|
|
22
|
+
code: country.code,
|
|
23
|
+
name: country.name,
|
|
24
|
+
})),
|
|
25
|
+
},
|
|
26
|
+
null,
|
|
27
|
+
2
|
|
28
|
+
),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
server.registerResource(
|
|
35
|
+
'categories',
|
|
36
|
+
'appstore://categories',
|
|
37
|
+
{
|
|
38
|
+
description: 'List of all App Store categories with their IDs',
|
|
39
|
+
mimeType: 'application/json',
|
|
40
|
+
},
|
|
41
|
+
async (uri) => ({
|
|
42
|
+
contents: [
|
|
43
|
+
{
|
|
44
|
+
uri: uri.href,
|
|
45
|
+
mimeType: 'application/json',
|
|
46
|
+
text: JSON.stringify(
|
|
47
|
+
{
|
|
48
|
+
categories: APP_STORE_CATEGORIES.map((category) => ({
|
|
49
|
+
id: category.id,
|
|
50
|
+
name: category.name,
|
|
51
|
+
emoji: category.emoji,
|
|
52
|
+
})),
|
|
53
|
+
},
|
|
54
|
+
null,
|
|
55
|
+
2
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
server.registerResource(
|
|
63
|
+
'platforms',
|
|
64
|
+
'appstore://platforms',
|
|
65
|
+
{
|
|
66
|
+
description: 'List of supported App Store platforms',
|
|
67
|
+
mimeType: 'application/json',
|
|
68
|
+
},
|
|
69
|
+
async (uri) => ({
|
|
70
|
+
contents: [
|
|
71
|
+
{
|
|
72
|
+
uri: uri.href,
|
|
73
|
+
mimeType: 'application/json',
|
|
74
|
+
text: JSON.stringify(
|
|
75
|
+
{
|
|
76
|
+
platforms: PLATFORMS.map((platform) => ({
|
|
77
|
+
value: platform.value,
|
|
78
|
+
label: platform.label,
|
|
79
|
+
emoji: platform.emoji,
|
|
80
|
+
})),
|
|
81
|
+
},
|
|
82
|
+
null,
|
|
83
|
+
2
|
|
84
|
+
),
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
server.registerResource(
|
|
91
|
+
'pricing-types',
|
|
92
|
+
'appstore://pricing-types',
|
|
93
|
+
{
|
|
94
|
+
description: 'List of App Store pricing types (free, paid, grossing)',
|
|
95
|
+
mimeType: 'application/json',
|
|
96
|
+
},
|
|
97
|
+
async (uri) => ({
|
|
98
|
+
contents: [
|
|
99
|
+
{
|
|
100
|
+
uri: uri.href,
|
|
101
|
+
mimeType: 'application/json',
|
|
102
|
+
text: JSON.stringify(
|
|
103
|
+
{
|
|
104
|
+
pricingTypes: PRICING_TYPES.map((pricing) => ({
|
|
105
|
+
value: pricing.value,
|
|
106
|
+
label: pricing.label,
|
|
107
|
+
emoji: pricing.emoji,
|
|
108
|
+
})),
|
|
109
|
+
},
|
|
110
|
+
null,
|
|
111
|
+
2
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { AppStoreService } from '../../services/app-store-service';
|
|
4
|
+
|
|
5
|
+
type InfoType = 'basic' | 'full' | 'release' | 'screenshots';
|
|
6
|
+
|
|
7
|
+
function formatDate(dateString?: string): string | null {
|
|
8
|
+
if (!dateString) return null;
|
|
9
|
+
const date = new Date(dateString);
|
|
10
|
+
return date.toLocaleDateString('en-US', {
|
|
11
|
+
year: 'numeric',
|
|
12
|
+
month: 'long',
|
|
13
|
+
day: 'numeric',
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function registerGetAppInfo(server: McpServer) {
|
|
18
|
+
server.registerTool(
|
|
19
|
+
'get_app_info',
|
|
20
|
+
{
|
|
21
|
+
description:
|
|
22
|
+
'Get information about a specific app by its App Store ID. Use the "include" parameter to specify what data to return.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
appId: z
|
|
25
|
+
.string()
|
|
26
|
+
.describe('The App Store ID of the app (numeric ID from the App Store URL)'),
|
|
27
|
+
country: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Country code for the App Store (e.g., US, GB, JP). Defaults to US'),
|
|
31
|
+
include: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe(
|
|
35
|
+
'What info to include: "basic" (name, artist, icon, price, rating), "full" (all details including description), "release" (release dates and version), "screenshots" (screenshot URLs). Defaults to "basic"'
|
|
36
|
+
),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
async ({ appId, country, include }) => {
|
|
40
|
+
const countryCode = country || 'US';
|
|
41
|
+
const infoType = (include || 'basic') as InfoType;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const app = await AppStoreService.getAppDetails(appId, countryCode);
|
|
45
|
+
|
|
46
|
+
if (!app) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text' as const,
|
|
51
|
+
text: JSON.stringify(
|
|
52
|
+
{
|
|
53
|
+
success: false,
|
|
54
|
+
error: `App with ID ${appId} not found`,
|
|
55
|
+
},
|
|
56
|
+
null,
|
|
57
|
+
2
|
|
58
|
+
),
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let result: Record<string, unknown> = {
|
|
65
|
+
success: true,
|
|
66
|
+
appId: app.id,
|
|
67
|
+
country: countryCode,
|
|
68
|
+
include: infoType,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
switch (infoType) {
|
|
72
|
+
case 'basic':
|
|
73
|
+
result.app = {
|
|
74
|
+
id: app.id,
|
|
75
|
+
name: app.name,
|
|
76
|
+
artistName: app.artistName,
|
|
77
|
+
url: app.url,
|
|
78
|
+
iconUrl: app.iconUrl,
|
|
79
|
+
formattedPrice: app.formattedPrice || 'Free',
|
|
80
|
+
rating: app.averageUserRating?.toFixed(1) || 'N/A',
|
|
81
|
+
ratingCount: app.userRatingCount || 0,
|
|
82
|
+
primaryGenre: app.primaryGenreName,
|
|
83
|
+
};
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'full':
|
|
87
|
+
result.app = {
|
|
88
|
+
id: app.id,
|
|
89
|
+
name: app.name,
|
|
90
|
+
artistName: app.artistName,
|
|
91
|
+
bundleId: app.bundleId,
|
|
92
|
+
url: app.url,
|
|
93
|
+
iconUrl: app.iconUrl,
|
|
94
|
+
description: app.description,
|
|
95
|
+
version: app.version,
|
|
96
|
+
price: app.price,
|
|
97
|
+
formattedPrice: app.formattedPrice || 'Free',
|
|
98
|
+
primaryGenre: app.primaryGenreName,
|
|
99
|
+
genres: app.genres,
|
|
100
|
+
rating: app.averageUserRating?.toFixed(1) || 'N/A',
|
|
101
|
+
ratingCount: app.userRatingCount || 0,
|
|
102
|
+
releaseDate: app.releaseDate,
|
|
103
|
+
releaseDateFormatted: formatDate(app.releaseDate),
|
|
104
|
+
currentVersionReleaseDate: app.currentVersionReleaseDate,
|
|
105
|
+
currentVersionReleaseDateFormatted: formatDate(
|
|
106
|
+
app.currentVersionReleaseDate
|
|
107
|
+
),
|
|
108
|
+
minimumOsVersion: app.minimumOsVersion,
|
|
109
|
+
fileSizeBytes: app.fileSizeBytes,
|
|
110
|
+
contentAdvisoryRating: app.contentAdvisoryRating,
|
|
111
|
+
screenshotCount: app.screenshotUrls?.length || 0,
|
|
112
|
+
ipadScreenshotCount: app.ipadScreenshotUrls?.length || 0,
|
|
113
|
+
};
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case 'release':
|
|
117
|
+
result.release = {
|
|
118
|
+
currentVersion: app.version,
|
|
119
|
+
originalReleaseDate: app.releaseDate,
|
|
120
|
+
originalReleaseDateFormatted: formatDate(app.releaseDate),
|
|
121
|
+
currentVersionReleaseDate: app.currentVersionReleaseDate,
|
|
122
|
+
currentVersionReleaseDateFormatted: formatDate(
|
|
123
|
+
app.currentVersionReleaseDate
|
|
124
|
+
),
|
|
125
|
+
};
|
|
126
|
+
break;
|
|
127
|
+
|
|
128
|
+
case 'screenshots':
|
|
129
|
+
result.screenshots = {
|
|
130
|
+
iphone: {
|
|
131
|
+
count: app.screenshotUrls?.length || 0,
|
|
132
|
+
urls: app.screenshotUrls || [],
|
|
133
|
+
},
|
|
134
|
+
ipad: {
|
|
135
|
+
count: app.ipadScreenshotUrls?.length || 0,
|
|
136
|
+
urls: app.ipadScreenshotUrls || [],
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
default:
|
|
142
|
+
result.app = {
|
|
143
|
+
id: app.id,
|
|
144
|
+
name: app.name,
|
|
145
|
+
artistName: app.artistName,
|
|
146
|
+
url: app.url,
|
|
147
|
+
iconUrl: app.iconUrl,
|
|
148
|
+
formattedPrice: app.formattedPrice || 'Free',
|
|
149
|
+
rating: app.averageUserRating?.toFixed(1) || 'N/A',
|
|
150
|
+
ratingCount: app.userRatingCount || 0,
|
|
151
|
+
primaryGenre: app.primaryGenreName,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: 'text' as const,
|
|
159
|
+
text: JSON.stringify(result, null, 2),
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
165
|
+
return {
|
|
166
|
+
content: [
|
|
167
|
+
{
|
|
168
|
+
type: 'text' as const,
|
|
169
|
+
text: JSON.stringify(
|
|
170
|
+
{
|
|
171
|
+
success: false,
|
|
172
|
+
error: errorMessage,
|
|
173
|
+
},
|
|
174
|
+
null,
|
|
175
|
+
2
|
|
176
|
+
),
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { AppStoreService } from '../../services/app-store-service';
|
|
4
|
+
import { APP_STORE_CATEGORIES, Platform, PricingType } from '../../types';
|
|
5
|
+
|
|
6
|
+
export function registerGetTrendingApps(server: McpServer) {
|
|
7
|
+
server.registerTool(
|
|
8
|
+
'get_trending_apps',
|
|
9
|
+
{
|
|
10
|
+
description: 'Get top/trending apps from the App Store by category, platform, and pricing type',
|
|
11
|
+
inputSchema: {
|
|
12
|
+
platform: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Platform to get apps for: iphone, ipad, mac, or tv. Defaults to iphone'),
|
|
16
|
+
pricingType: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe(
|
|
20
|
+
'Pricing type: free (top free apps), paid (top paid apps), or grossing (top grossing apps). Defaults to free'
|
|
21
|
+
),
|
|
22
|
+
country: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Country code for the App Store (e.g., US, GB, JP). Defaults to US'),
|
|
26
|
+
categoryId: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe(
|
|
30
|
+
'Optional category ID to filter by (e.g., 6014 for Games, 6007 for Productivity)'
|
|
31
|
+
),
|
|
32
|
+
limit: z
|
|
33
|
+
.number()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('Maximum number of results to return (1-100). Defaults to 25'),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
async ({ platform, pricingType, country, categoryId, limit }) => {
|
|
39
|
+
const platformValue = (platform || 'iphone') as Platform;
|
|
40
|
+
const pricingValue = (pricingType || 'free') as PricingType;
|
|
41
|
+
const countryCode = country || 'US';
|
|
42
|
+
const resultLimit = limit || 25;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const apps = await AppStoreService.getTrendingApps(
|
|
46
|
+
platformValue,
|
|
47
|
+
pricingValue,
|
|
48
|
+
countryCode,
|
|
49
|
+
categoryId,
|
|
50
|
+
resultLimit
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const categoryInfo = categoryId
|
|
54
|
+
? APP_STORE_CATEGORIES.find((c) => c.id === categoryId)
|
|
55
|
+
: null;
|
|
56
|
+
|
|
57
|
+
const result = {
|
|
58
|
+
success: true,
|
|
59
|
+
count: apps.length,
|
|
60
|
+
filters: {
|
|
61
|
+
platform: platformValue,
|
|
62
|
+
pricingType: pricingValue,
|
|
63
|
+
country: countryCode,
|
|
64
|
+
category: categoryInfo
|
|
65
|
+
? { id: categoryInfo.id, name: categoryInfo.name }
|
|
66
|
+
: 'All Categories',
|
|
67
|
+
},
|
|
68
|
+
apps: apps.map((app, index) => ({
|
|
69
|
+
rank: index + 1,
|
|
70
|
+
id: app.id,
|
|
71
|
+
name: app.name,
|
|
72
|
+
artistName: app.artistName,
|
|
73
|
+
url: app.url,
|
|
74
|
+
iconUrl: app.iconUrl,
|
|
75
|
+
price: app.formattedPrice || 'Free',
|
|
76
|
+
primaryGenre: app.primaryGenreName,
|
|
77
|
+
})),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: 'text' as const,
|
|
84
|
+
text: JSON.stringify(result, null, 2),
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{
|
|
93
|
+
type: 'text' as const,
|
|
94
|
+
text: JSON.stringify(
|
|
95
|
+
{
|
|
96
|
+
success: false,
|
|
97
|
+
error: errorMessage,
|
|
98
|
+
},
|
|
99
|
+
null,
|
|
100
|
+
2
|
|
101
|
+
),
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { registerSearchApps } from './search-apps';
|
|
3
|
+
import { registerGetAppInfo } from './get-app-info';
|
|
4
|
+
import { registerGetTrendingApps } from './get-trending-apps';
|
|
5
|
+
|
|
6
|
+
export function registerAllTools(server: McpServer) {
|
|
7
|
+
registerSearchApps(server);
|
|
8
|
+
registerGetAppInfo(server);
|
|
9
|
+
registerGetTrendingApps(server);
|
|
10
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { AppStoreService } from '../../services/app-store-service';
|
|
4
|
+
|
|
5
|
+
export function registerSearchApps(server: McpServer) {
|
|
6
|
+
server.registerTool(
|
|
7
|
+
'search_apps',
|
|
8
|
+
{
|
|
9
|
+
description: 'Search for apps on the App Store by name or keyword',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
query: z.string().describe('The search term to find apps'),
|
|
12
|
+
country: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Country code for the App Store (e.g., US, GB, JP). Defaults to US'),
|
|
16
|
+
limit: z
|
|
17
|
+
.number()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Maximum number of results to return (1-200). Defaults to 25'),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
async ({ query, country, limit }) => {
|
|
23
|
+
const countryCode = country || 'US';
|
|
24
|
+
const resultLimit = limit || 25;
|
|
25
|
+
try {
|
|
26
|
+
const apps = await AppStoreService.searchApps(query, countryCode, resultLimit);
|
|
27
|
+
|
|
28
|
+
const result = {
|
|
29
|
+
success: true,
|
|
30
|
+
count: apps.length,
|
|
31
|
+
country: countryCode,
|
|
32
|
+
query: query,
|
|
33
|
+
apps: apps.map((app) => ({
|
|
34
|
+
id: app.id,
|
|
35
|
+
name: app.name,
|
|
36
|
+
artistName: app.artistName,
|
|
37
|
+
url: app.url,
|
|
38
|
+
iconUrl: app.iconUrl,
|
|
39
|
+
price: app.formattedPrice || 'Free',
|
|
40
|
+
rating: app.averageUserRating?.toFixed(1) || 'N/A',
|
|
41
|
+
ratingCount: app.userRatingCount || 0,
|
|
42
|
+
primaryGenre: app.primaryGenreName,
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: 'text' as const,
|
|
50
|
+
text: JSON.stringify(result, null, 2),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: 'text' as const,
|
|
60
|
+
text: JSON.stringify(
|
|
61
|
+
{
|
|
62
|
+
success: false,
|
|
63
|
+
error: errorMessage,
|
|
64
|
+
},
|
|
65
|
+
null,
|
|
66
|
+
2
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|