App Market Intelligence MCP
by JiantaoFu
Verified
#!/usr/bin/env node
/**
* MIT License
*
* Copyright (c) 2024 Insightly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import store from '@jeromyfu/app-store-scraper';
import gplay from '@jeromyfu/google-play-scraper';
import { z } from 'zod';
const server = new McpServer({
name: "app-info-scraper",
version: "1.0.0"
});
// App Store Tools
server.tool("app-store-search",
"Search for apps on the App Store. Returns a list of apps with the following fields:\n" +
"- id: App Store ID number\n" +
"- appId: Bundle ID (e.g. 'com.company.app')\n" +
"- title: App name\n" +
"- icon: Icon image URL\n" +
"- url: App Store URL\n" +
"- price: Price in USD\n" +
"- currency: Price currency code\n" +
"- free: Boolean indicating if app is free\n" +
"- description: App description\n" +
"- developer: Developer name\n" +
"- developerUrl: Developer's App Store URL\n" +
"- developerId: Developer's ID\n" +
"- genre: App category name\n" +
"- genreId: Category ID\n" +
"- released: Release date (ISO string)",
{
term: z.string().describe("Search term (required)"),
num: z.number().default(50).describe("Number of results to retrieve (default: 50)"),
page: z.number().default(1).describe("Page of results to retrieve (default: 1)"),
country: z.string().default("us").describe("Two letter country code (default: us)"),
lang: z.string().default("en-us").describe("Language code for result text (default: en-us)"),
idsOnly: z.boolean().default(false).describe("Skip extra lookup request. Returns array of application IDs only (default: false)")
},
async ({ term, num, page, country, lang, idsOnly }) => {
const results = await store.search({ term, num, page, country, lang, idsOnly });
return { content: [{ type: "text", text: JSON.stringify(results) }] };
}
);
server.tool("app-store-details",
"Get detailed information about an App Store app. Returns an object with:\n" +
"- id: App Store ID number\n" +
"- appId: Bundle ID (e.g. 'com.company.app')\n" +
"- title: App name\n" +
"- url: App Store URL\n" +
"- description: Full app description\n" +
"- icon: Icon URL\n" +
"- genres: Array of category names\n" +
"- genreIds: Array of category IDs\n" +
"- primaryGenre: Main category name\n" +
"- primaryGenreId: Main category ID\n" +
"- contentRating: Content rating (e.g. '4+')\n" +
"- languages: Array of language codes\n" +
"- size: App size in bytes\n" +
"- requiredOsVersion: Minimum iOS version required\n" +
"- released: Initial release date (ISO string)\n" +
"- updated: Last update date (ISO string)\n" +
"- releaseNotes: Latest version changes\n" +
"- version: Current version string\n" +
"- price: Price in USD\n" +
"- currency: Price currency code\n" +
"- free: Boolean indicating if app is free\n" +
"- developerId: Developer's ID\n" +
"- developer: Developer name\n" +
"- developerUrl: Developer's App Store URL\n" +
"- developerWebsite: Developer's website URL if available\n" +
"- score: Current rating (0-5)\n" +
"- reviews: Total number of ratings\n" +
"- currentVersionScore: Current version rating (0-5)\n" +
"- currentVersionReviews: Current version review count\n" +
"- screenshots: Array of screenshot URLs\n" +
"- ipadScreenshots: Array of iPad screenshot URLs\n" +
"- appletvScreenshots: Array of Apple TV screenshot URLs\n" +
"- supportedDevices: Array of supported device IDs\n" +
"- ratings: Total number of ratings (when ratings option enabled)\n" +
"- histogram: Rating distribution by star level (when ratings option enabled)",
{
id: z.number().optional().describe("Numeric App ID (e.g., 553834731). Either this or appId must be provided."),
appId: z.string().optional().describe("Bundle ID (e.g., 'com.midasplayer.apps.candycrushsaga'). Either this or id must be provided."),
country: z.string().default("us").describe("Country code to get app details from (default: us). Also affects data language."),
lang: z.string().optional().describe("Language code for result text. If not provided, uses country-specific language."),
ratings: z.boolean().optional().default(false).describe("Load additional ratings information like ratings count and histogram")
},
async ({ id, appId, country, lang, ratings }) => {
const details = await store.app({ id, appId, country, lang, ratings });
return { content: [{ type: "text", text: JSON.stringify(details) }] };
}
);
server.tool("app-store-reviews",
"Get reviews for an App Store app. Returns an array of reviews with:\n" +
"- id: Review ID\n" +
"- userName: Reviewer's name\n" +
"- userUrl: Reviewer's profile URL\n" +
"- version: App version reviewed\n" +
"- score: Rating (1-5)\n" +
"- title: Review title\n" +
"- text: Review content\n" +
"- url: Review URL\n" +
"- updated: Review date (ISO string)",
{
id: z.number().optional().describe("Numeric App ID (e.g., 553834731). Either this or appId must be provided."),
appId: z.string().optional().describe("Bundle ID (e.g., 'com.midasplayer.apps.candycrushsaga'). Either this or id must be provided."),
country: z.string().default("us").describe("Country code to get reviews from (default: us)"),
page: z.number().min(1).max(10).default(1).describe("Page number to retrieve (default: 1, max: 10)"),
sort: z.enum(["recent", "helpful"]).default("recent").describe("Sort order (recent or helpful)")
},
async ({ id, appId, country, page, sort }) => {
const reviews = await store.reviews({
id,
appId,
country,
page,
sort: sort === "helpful" ? store.sort.HELPFUL : store.sort.RECENT
});
return { content: [{ type: "text", text: JSON.stringify(reviews) }] };
}
);
server.tool("app-store-similar",
"Get similar apps ('customers also bought') from the App Store. Returns a list of apps with:\n" +
"- id: App Store ID number\n" +
"- appId: Bundle ID (e.g. 'com.company.app')\n" +
"- title: App name\n" +
"- icon: Icon image URL\n" +
"- url: App Store URL\n" +
"- price: Price in USD\n" +
"- currency: Price currency code\n" +
"- free: Boolean indicating if app is free\n" +
"- description: App description\n" +
"- developer: Developer name\n" +
"- developerUrl: Developer's App Store URL\n" +
"- developerId: Developer's ID\n" +
"- genre: App category name\n" +
"- genreId: Category ID\n" +
"- released: Release date (ISO string)",
{
id: z.number().optional().describe("Numeric App ID (e.g., 553834731). Either this or appId must be provided."),
appId: z.string().optional().describe("Bundle ID (e.g., 'com.midasplayer.apps.candycrushsaga'). Either this or id must be provided.")
},
async ({ id, appId }) => {
const similar = await store.similar({ id, appId });
return { content: [{ type: "text", text: JSON.stringify(similar) }] };
}
);
// Additional App Store Tools
server.tool("app-store-developer",
"Get apps by a developer on the App Store. Returns a list of apps with:\n" +
"- id: App Store ID number\n" +
"- appId: Bundle ID (e.g. 'com.company.app')\n" +
"- title: App name\n" +
"- icon: Icon image URL\n" +
"- url: App Store URL\n" +
"- price: Price in USD\n" +
"- currency: Price currency code\n" +
"- free: Boolean indicating if app is free\n" +
"- description: App description\n" +
"- developer: Developer name\n" +
"- developerUrl: Developer's App Store URL\n" +
"- developerId: Developer's ID\n" +
"- genre: App category name\n" +
"- genreId: Category ID\n" +
"- released: Release date (ISO string)",
{
devId: z.string().describe("iTunes artist ID of the developer (e.g., 284882218 for Facebook)"),
country: z.string().default("us").describe("Country code to get app details from (default: us). Also affects data language."),
lang: z.string().optional().describe("Language code for result text. If not provided, uses country-specific language.")
},
async ({ devId, country, lang }) => {
const apps = await store.developer({ devId, country, lang });
return { content: [{ type: "text", text: JSON.stringify(apps) }] };
}
);
server.tool("app-store-suggest",
"Get search suggestions from the App Store. Returns an array of objects with:\n" +
"- term: Suggested search term\n" +
"Each suggestion has a priority from 0 (low traffic) to 10000 (most searched)",
{
term: z.string().describe("Search term to get suggestions for")
},
async ({ term, country }) => {
const suggestions = await store.suggest({ term });
return { content: [{ type: "text", text: JSON.stringify(suggestions) }] };
}
);
server.tool("app-store-ratings",
"Get ratings for an App Store app. Returns an object with:\n" +
"- ratings: Total number of ratings\n" +
"- histogram: Distribution of ratings by star level (1-5)",
{
id: z.number().optional().describe("Numeric App ID (e.g., 553834731). Either this or appId must be provided."),
appId: z.string().optional().describe("Bundle ID (e.g., 'com.midasplayer.apps.candycrushsaga'). Either this or id must be provided."),
country: z.string().default("us").describe("Country code to get ratings from (default: us)")
},
async ({ id, appId, country }) => {
const ratings = await store.ratings({ id, appId, country });
return { content: [{ type: "text", text: JSON.stringify(ratings) }] };
}
);
server.tool("app-store-version-history",
"Get version history for an App Store app. Returns an array of versions with:\n" +
"- versionDisplay: Version number string\n" +
"- releaseNotes: Update description\n" +
"- releaseDate: Release date (YYYY-MM-DD)\n" +
"- releaseTimestamp: Release date and time (ISO string)",
{
id: z.number().describe("Numeric App ID (e.g., 444934666)")
},
async ({ id }) => {
const history = await store.versionHistory({ id });
return { content: [{ type: "text", text: JSON.stringify(history) }] };
}
);
server.tool("app-store-privacy",
"Get privacy details for an App Store app. Returns an object with:\n" +
"- managePrivacyChoicesUrl: URL to manage privacy choices (if available)\n" +
"- privacyTypes: Array of privacy data types, each containing:\n" +
" - privacyType: Name of the privacy category\n" +
" - identifier: Unique identifier for the privacy type\n" +
" - description: Detailed description of how data is used\n" +
" - dataCategories: Array of data categories, each containing:\n" +
" - dataCategory: Category name\n" +
" - identifier: Category identifier\n" +
" - dataTypes: Array of specific data types collected\n" +
" - purposes: Array of purposes for data collection\n" +
"Note: Currently only available for US App Store.",
{
id: z.number().describe("Numeric App ID (e.g., 553834731)")
},
async ({ id }) => {
const privacy = await store.privacy({ id });
return { content: [{ type: "text", text: JSON.stringify(privacy) }] };
}
);
server.tool("app-store-list",
"Get apps from iTunes collections. Returns a list of apps with:\n" +
"- id: App Store ID number\n" +
"- appId: Bundle ID (e.g. 'com.company.app')\n" +
"- title: App name\n" +
"- icon: Icon image URL\n" +
"- url: App Store URL\n" +
"- price: Price in USD\n" +
"- currency: Price currency code\n" +
"- free: Boolean indicating if app is free\n" +
"- description: App description\n" +
"- developer: Developer name\n" +
"- developerUrl: Developer's App Store URL\n" +
"- developerId: Developer's ID\n" +
"- genre: App category name\n" +
"- genreId: Category ID\n" +
"- released: Release date (ISO string)",
{
collection: z.enum([
'newapplications',
'newfreeapplications',
'newpaidapplications',
'topfreeapplications',
'topfreeipadapplications',
'topgrossingapplications',
'topgrossingipadapplications',
'toppaidapplications',
'toppaidipadapplications'
]).describe(
"Collection to fetch from. Available collections:\n" +
"- newapplications: New iOS applications\n" +
"- newfreeapplications: New free iOS applications\n" +
"- newpaidapplications: New paid iOS applications\n" +
"- topfreeapplications: Top free iOS applications\n" +
"- topfreeipadapplications: Top free iPad applications\n" +
"- topgrossingapplications: Top grossing iOS applications\n" +
"- topgrossingipadapplications: Top grossing iPad applications\n" +
"- toppaidapplications: Top paid iOS applications\n" +
"- toppaidipadapplications: Top paid iPad applications"
),
category: z.number().optional().describe(
"Category ID to filter by. Available categories:\n" +
"Main Categories:\n" +
"- 6000: BUSINESS\n" +
"- 6001: WEATHER\n" +
"- 6002: UTILITIES\n" +
"- 6003: TRAVEL\n" +
"- 6004: SPORTS\n" +
"- 6005: SOCIAL_NETWORKING\n" +
"- 6006: REFERENCE\n" +
"- 6007: PRODUCTIVITY\n" +
"- 6008: PHOTO_AND_VIDEO\n" +
"- 6009: NEWS\n" +
"- 6010: NAVIGATION\n" +
"- 6011: MUSIC\n" +
"- 6012: LIFESTYLE\n" +
"- 6013: HEALTH_AND_FITNESS\n" +
"- 6014: GAMES\n" +
"- 6015: FINANCE\n" +
"- 6016: ENTERTAINMENT\n" +
"- 6017: EDUCATION\n" +
"- 6018: BOOKS\n" +
"- 6020: MEDICAL\n" +
"- 6021: MAGAZINES_AND_NEWSPAPERS\n" +
"- 6022: CATALOGS\n" +
"- 6023: FOOD_AND_DRINK\n" +
"- 6024: SHOPPING\n\n" +
"Games Subcategories:\n" +
"- 7001: ACTION\n" +
"- 7002: ADVENTURE\n" +
"- 7003: ARCADE\n" +
"- 7004: BOARD\n" +
"- 7005: CARD\n" +
"- 7006: CASINO\n" +
"- 7007: DICE\n" +
"- 7008: EDUCATIONAL\n" +
"- 7009: FAMILY\n" +
"- 7011: MUSIC\n" +
"- 7012: PUZZLE\n" +
"- 7013: RACING\n" +
"- 7014: ROLE_PLAYING\n" +
"- 7015: SIMULATION\n" +
"- 7016: SPORTS\n" +
"- 7017: STRATEGY\n" +
"- 7018: TRIVIA\n" +
"- 7019: WORD\n\n" +
"Magazine Subcategories:\n" +
"- 13001: POLITICS\n" +
"- 13002: FASHION\n" +
"- 13003: HOME\n" +
"- 13004: OUTDOORS\n" +
"- 13005: SPORTS\n" +
"- 13006: AUTOMOTIVE\n" +
"- 13007: ARTS\n" +
"- 13008: WEDDINGS\n" +
"- 13009: BUSINESS\n" +
"- 13010: CHILDREN\n" +
"- 13011: COMPUTER\n" +
"- 13012: FOOD\n" +
"- 13013: CRAFTS\n" +
"- 13014: ELECTRONICS\n" +
"- 13015: ENTERTAINMENT\n" +
"- 13017: HEALTH\n" +
"- 13018: HISTORY\n" +
"- 13019: LITERARY\n" +
"- 13020: MEN\n" +
"- 13021: MOVIES_AND_MUSIC\n" +
"- 13023: FAMILY\n" +
"- 13024: PETS\n" +
"- 13025: PROFESSIONAL\n" +
"- 13026: REGIONAL\n" +
"- 13027: SCIENCE\n" +
"- 13028: TEENS\n" +
"- 13029: TRAVEL\n" +
"- 13030: WOMEN"
),
lang: z.string().optional().describe("Language code for result text. If not provided, uses country-specific language."),
fullDetail: z.boolean().default(false).describe("Get full app details including ratings, reviews etc (default: false)"),
country: z.string().default("us").describe("Country code (default: us)"),
num: z.number().max(200).default(50).describe("Number of results (default: 50, max: 200)")
},
async ({ collection, category, country, num, lang, fullDetail }) => {
const results = await store.list({ collection, category, country, num, lang, fullDetail });
return { content: [{ type: "text", text: JSON.stringify(results) }] };
}
);
// Google Play Tools
server.tool("google-play-search",
"Search for apps on Google Play. Returns a list of apps with:\n" +
"- title: App name\n" +
"- appId: Package name (e.g. 'com.company.app')\n" +
"- url: Play Store URL\n" +
"- icon: Icon image URL\n" +
"- developer: Developer name\n" +
"- developerId: Developer ID\n" +
"- priceText: Price display text\n" +
"- free: Boolean indicating if app is free\n" +
"- summary: Short description\n" +
"- scoreText: Rating display text\n" +
"- score: Rating (0-5)",
{
term: z.string().describe("Search term to query apps"),
price: z.enum(["all", "free", "paid"]).default("all").describe("Filter by price: all, free, or paid (default: all)"),
num: z.number().default(20).describe("Number of results to retrieve (default: 20, max: 250)"),
lang: z.string().default("en").describe("Language code for result text (default: en)"),
country: z.string().default("us").describe("Country code to get results from (default: us)"),
fullDetail: z.boolean().default(false).describe("Include full app details in results (default: false)")
},
async ({ term, price, num, lang, country, fullDetail }) => {
const results = await gplay.search({ term, price, num, lang, country, fullDetail });
return { content: [{ type: "text", text: JSON.stringify(results) }] };
}
);
server.tool("google-play-details",
"Get detailed information about a Google Play app. Returns an object with:\n" +
"- title: App name\n" +
"- description: Full app description\n" +
"- descriptionHTML: Description with HTML formatting\n" +
"- summary: Short description\n" +
"- installs: Install count range\n" +
"- minInstalls: Minimum install count\n" +
"- maxInstalls: Maximum install count\n" +
"- score: Average rating (0-5)\n" +
"- scoreText: Rating display text\n" +
"- ratings: Total number of ratings\n" +
"- reviews: Total number of reviews\n" +
"- histogram: Rating distribution by star level\n" +
"- price: Price in local currency\n" +
"- free: Boolean indicating if app is free\n" +
"- currency: Price currency code\n" +
"- priceText: Formatted price string\n" +
"- offersIAP: Boolean indicating in-app purchases\n" +
"- IAPRange: Price range for in-app purchases\n" +
"- androidVersion: Minimum Android version required\n" +
"- androidVersionText: Formatted Android version text\n" +
"- developer: Developer name\n" +
"- developerId: Developer ID\n" +
"- developerEmail: Developer contact email\n" +
"- developerWebsite: Developer website URL\n" +
"- developerAddress: Developer physical address\n" +
"- genre: App category\n" +
"- genreId: Category ID\n" +
"- icon: Icon URL\n" +
"- headerImage: Feature graphic URL\n" +
"- screenshots: Array of screenshot URLs\n" +
"- contentRating: Content rating (e.g. 'Everyone')\n" +
"- contentRatingDescription: Content rating details\n" +
"- adSupported: Boolean indicating if app shows ads\n" +
"- released: Release date\n" +
"- updated: Last update date\n" +
"- version: Current version string\n" +
"- recentChanges: Latest version changes\n" +
"- preregister: Boolean indicating if app is in pre-registration\n" +
"- editorsChoice: Boolean indicating Editor's Choice status\n" +
"- features: Array of special features",
{
appId: z.string().describe("Google Play package name (e.g., 'com.google.android.apps.translate')"),
lang: z.string().default("en").describe("Language code for result text (default: en)"),
country: z.string().default("us").describe("Country code to check app availability (default: us)")
},
async ({ appId, lang, country }) => {
const details = await gplay.app({ appId, lang, country });
return { content: [{ type: "text", text: JSON.stringify(details) }] };
}
);
server.tool("google-play-reviews",
"Get reviews for a Google Play app. Returns an array of reviews with:\n" +
"- id: Review ID string\n" +
"- userName: Reviewer's name\n" +
"- userImage: Reviewer's profile image URL\n" +
"- date: Review date (ISO string)\n" +
"- score: Rating (1-5)\n" +
"- scoreText: Rating display text\n" +
"- title: Review title\n" +
"- text: Review content\n" +
"- url: Review URL\n" +
"- version: App version reviewed\n" +
"- thumbsUp: Number of thumbs up votes\n" +
"- replyDate: Developer reply date (if any)\n" +
"- replyText: Developer reply content (if any)\n" +
"- criterias: Array of rating criteria (if any)\n" +
"\nNote: Reviews are returned in the specified language. The total review count\n" +
"shown in Google Play refers to ratings, not written reviews.",
{
appId: z.string().describe("Package name of the app (e.g., 'com.mojang.minecraftpe')"),
lang: z.string().default("en").describe("Language code for reviews (default: en)"),
country: z.string().default("us").describe("Country code (default: us)"),
sort: z.enum(["newest", "rating", "helpfulness"]).default("newest")
.describe("Sort order: newest, rating, or helpfulness (default: newest)"),
num: z.number().default(100).describe("Number of reviews to retrieve (default: 100). Ignored if paginate is true."),
paginate: z.boolean().default(false).describe("Enable pagination with 150 reviews per page"),
nextPaginationToken: z.string().optional().describe("Token for fetching next page of reviews")
},
async ({ appId, lang, country, sort, num, paginate, nextPaginationToken }) => {
const sortMap = {
newest: gplay.sort.NEWEST,
rating: gplay.sort.RATING,
helpfulness: gplay.sort.HELPFULNESS
};
const reviews = await gplay.reviews({
appId,
lang,
country,
sort: sortMap[sort],
num,
paginate,
nextPaginationToken
});
return {
content: [{
type: "text",
text: JSON.stringify({
reviews: reviews.data,
nextPage: reviews.nextPaginationToken
})
}]
};
}
);
server.tool("google-play-similar",
"Get similar apps from Google Play. Returns a list of apps with:\n" +
"- url: Play Store URL\n" +
"- appId: Package name (e.g. 'com.company.app')\n" +
"- summary: Short description\n" +
"- developer: Developer name\n" +
"- developerId: Developer ID\n" +
"- icon: Icon image URL\n" +
"- score: Rating (0-5)\n" +
"- scoreText: Rating display text\n" +
"- priceText: Price display text\n" +
"- free: Boolean indicating if app is free\n",
{
appId: z.string().describe("Google Play package name (e.g., 'com.dxco.pandavszombies')"),
lang: z.string().default("en").describe("Language code for result text (default: en)"),
country: z.string().default("us").describe("Country code to get results from (default: us)"),
fullDetail: z.boolean().default(false).describe("Include full app details in results (default: false), If fullDetail is true, includes all fields from app details endpoint.")
},
async ({ appId, lang, country, fullDetail }) => {
const similar = await gplay.similar({ appId, lang, country, fullDetail });
return { content: [{ type: "text", text: JSON.stringify(similar) }] };
}
);
// Additional Google Play Tools
server.tool("google-play-developer",
"Get apps by a developer on Google Play. Returns a list of apps with:\n" +
"- url: Play Store URL\n" +
"- appId: Package name (e.g. 'com.company.app')\n" +
"- title: App name\n" +
"- summary: Short app description\n" +
"- developer: Developer name\n" +
"- developerId: Developer ID\n" +
"- icon: Icon image URL\n" +
"- score: Rating (0-5)\n" +
"- scoreText: Rating display text\n" +
"- priceText: Price display text\n" +
"- free: Boolean indicating if app is free\n",
{
devId: z.string().describe("Developer name (e.g., 'DxCo Games')"),
lang: z.string().default("en").describe("Language code for result text (default: en)"),
country: z.string().default("us").describe("Country code to get results from (default: us)"),
num: z.number().default(60).describe("Number of results to retrieve (default: 60)"),
fullDetail: z.boolean().default(false).describe("Include full app details in results (default: false), If fullDetail is true, includes all fields from app details endpoint.")
},
async ({ devId, lang, country, num, fullDetail }) => {
const apps = await gplay.developer({ devId, lang, country, num, fullDetail });
return { content: [{ type: "text", text: JSON.stringify(apps) }] };
}
);
server.tool("google-play-suggest",
"Get search suggestions from Google Play. Returns an array of suggested search terms (up to 5).\n" +
"Sample response: ['panda pop', 'panda', 'panda games', 'panda run', 'panda pop for free']",
{
term: z.string().describe("Search term to get suggestions for (e.g., 'panda')"),
lang: z.string().default("en").describe("Language code for suggestions (default: en)"),
country: z.string().default("us").describe("Country code to get suggestions from (default: us)")
},
async ({ term, lang, country }) => {
const suggestions = await gplay.suggest({ term, lang, country });
// API returns array of strings directly
return { content: [{ type: "text", text: JSON.stringify(suggestions) }] };
}
);
server.tool("google-play-permissions",
"Get permissions required by a Google Play app. Returns a list of permissions with:\n" +
"- permission: Description of the permission (e.g., 'modify storage contents')\n" +
"- type: Permission category (e.g., 'Storage', 'Network')\n\n" +
"When short=true, returns just an array of permission strings.\n" +
"Note: Permissions are returned in the specified language.",
{
appId: z.string().describe("Google Play package name (e.g., 'com.dxco.pandavszombies')"),
lang: z.string().default("en").describe("Language code for permission text (default: en)"),
country: z.string().default("us").describe("Country code to check app (default: us)"),
short: z.boolean().default(false).describe("Return only permission names without categories (default: false)")
},
async ({ appId, lang, country, short }) => {
const permissions = await gplay.permissions({ appId, lang, country, short });
return { content: [{ type: "text", text: JSON.stringify(permissions) }] };
}
);
server.tool("google-play-datasafety",
"Get data safety information for a Google Play app. Returns an object with:\n" +
"- dataShared: Array of shared data items, each containing:\n" +
" - data: Name of the data being shared (e.g., 'User IDs')\n" +
" - optional: Boolean indicating if sharing is optional\n" +
" - purpose: Comma-separated list of purposes (e.g., 'Analytics, Marketing')\n" +
" - type: Category of data (e.g., 'Personal info')\n" +
"- dataCollected: Array of collected data items with same structure as dataShared\n" +
"- securityPractices: Array of security practices, each containing:\n" +
" - practice: Name of the security practice\n" +
" - description: Detailed description of the practice\n" +
"- privacyPolicyUrl: URL to the app's privacy policy\n\n" +
"Data types can include: Personal info, Financial info, Messages, Contacts,\n" +
"App activity, App info and performance, Device or other IDs",
{
appId: z.string().describe("Google Play package name (e.g., 'com.dxco.pandavszombies')"),
lang: z.string().default("en").describe("Language code for data safety info (default: en)")
},
async ({ appId, lang }) => {
const datasafety = await gplay.datasafety({ appId, lang });
return { content: [{ type: "text", text: JSON.stringify(datasafety) }] };
}
);
server.tool("google-play-categories",
"Get list of all Google Play categories. Returns an array of category identifiers like:\n" +
"- 'APPLICATION': All applications\n" +
"- 'GAME': All games\n" +
"- 'ANDROID_WEAR': Wear OS apps\n" +
"- 'SOCIAL': Social apps\n" +
"- 'PRODUCTIVITY': Productivity apps\n" +
"etc.\n\n" +
"These category IDs can be used with the google-play-list tool to filter apps by category.\n" +
"Sample response: ['AUTO_AND_VEHICLES', 'LIBRARIES_AND_DEMO', 'LIFESTYLE', ...]",
{}, // No parameters needed
async () => {
const categories = await gplay.categories();
return { content: [{ type: "text", text: JSON.stringify(categories) }] };
}
);
server.tool("google-play-list",
"Get apps from Google Play collections. Returns a list of apps with:\n" +
"- url: Play Store URL\n" +
"- appId: Package name (e.g., 'com.company.app')\n" +
"- title: App name\n" +
"- summary: Short description\n" +
"- developer: Developer name\n" +
"- developerId: Developer ID\n" +
"- icon: Icon URL\n" +
"- score: Rating (0-5)\n" +
"- scoreText: Rating display text\n" +
"- priceText: Price display text\n" +
"- free: Boolean indicating if app is free\n\n" +
"When fullDetail is true, includes all fields from app details endpoint.",
{
collection: z.enum(['TOP_FREE', 'TOP_PAID', 'GROSSING', 'TOP_FREE_GAMES', 'TOP_PAID_GAMES', 'TOP_GROSSING_GAMES'])
.default('TOP_FREE')
.describe(
"Collection to fetch apps from (default: TOP_FREE). Available collections:\n" +
"- TOP_FREE: Top free applications\n" +
"- TOP_PAID: Top paid applications\n" +
"- GROSSING: Top grossing applications"
),
category: z.enum([
'APPLICATION',
'ANDROID_WEAR',
'ART_AND_DESIGN',
'AUTO_AND_VEHICLES',
'BEAUTY',
'BOOKS_AND_REFERENCE',
'BUSINESS',
'COMICS',
'COMMUNICATION',
'DATING',
'EDUCATION',
'ENTERTAINMENT',
'EVENTS',
'FINANCE',
'FOOD_AND_DRINK',
'HEALTH_AND_FITNESS',
'HOUSE_AND_HOME',
'LIBRARIES_AND_DEMO',
'LIFESTYLE',
'MAPS_AND_NAVIGATION',
'MEDICAL',
'MUSIC_AND_AUDIO',
'NEWS_AND_MAGAZINES',
'PARENTING',
'PERSONALIZATION',
'PHOTOGRAPHY',
'PRODUCTIVITY',
'SHOPPING',
'SOCIAL',
'SPORTS',
'TOOLS',
'TRAVEL_AND_LOCAL',
'VIDEO_PLAYERS',
'WATCH_FACE',
'WEATHER',
'GAME',
'GAME_ACTION',
'GAME_ADVENTURE',
'GAME_ARCADE',
'GAME_BOARD',
'GAME_CARD',
'GAME_CASINO',
'GAME_CASUAL',
'GAME_EDUCATIONAL',
'GAME_MUSIC',
'GAME_PUZZLE',
'GAME_RACING',
'GAME_ROLE_PLAYING',
'GAME_SIMULATION',
'GAME_SPORTS',
'GAME_STRATEGY',
'GAME_TRIVIA',
'GAME_WORD',
'FAMILY'
]).optional().describe(
"Category to filter by. Available categories:\n" +
"Main Categories:\n" +
"- APPLICATION: All applications\n" +
"- ANDROID_WEAR: Wear OS apps\n" +
"- ART_AND_DESIGN: Art & Design\n" +
"- AUTO_AND_VEHICLES: Auto & Vehicles\n" +
"- BEAUTY: Beauty\n" +
"- BOOKS_AND_REFERENCE: Books & Reference\n" +
"- BUSINESS: Business\n" +
"- COMICS: Comics\n" +
"- COMMUNICATION: Communication\n" +
"- DATING: Dating\n" +
"- EDUCATION: Education\n" +
"- ENTERTAINMENT: Entertainment\n" +
"- EVENTS: Events\n" +
"- FINANCE: Finance\n" +
"- FOOD_AND_DRINK: Food & Drink\n" +
"- HEALTH_AND_FITNESS: Health & Fitness\n" +
"- HOUSE_AND_HOME: House & Home\n" +
"- LIFESTYLE: Lifestyle\n" +
"- MAPS_AND_NAVIGATION: Maps & Navigation\n" +
"- MEDICAL: Medical\n" +
"- MUSIC_AND_AUDIO: Music & Audio\n" +
"- NEWS_AND_MAGAZINES: News & Magazines\n" +
"- PARENTING: Parenting\n" +
"- PERSONALIZATION: Personalization\n" +
"- PHOTOGRAPHY: Photography\n" +
"- PRODUCTIVITY: Productivity\n" +
"- SHOPPING: Shopping\n" +
"- SOCIAL: Social\n" +
"- SPORTS: Sports\n" +
"- TOOLS: Tools\n" +
"- TRAVEL_AND_LOCAL: Travel & Local\n" +
"- VIDEO_PLAYERS: Video Players\n" +
"- WATCH_FACE: Watch Faces\n" +
"- WEATHER: Weather\n\n" +
"Game Categories:\n" +
"- GAME: All Games\n" +
"- GAME_ACTION: Action Games\n" +
"- GAME_ADVENTURE: Adventure Games\n" +
"- GAME_ARCADE: Arcade Games\n" +
"- GAME_BOARD: Board Games\n" +
"- GAME_CARD: Card Games\n" +
"- GAME_CASINO: Casino Games\n" +
"- GAME_CASUAL: Casual Games\n" +
"- GAME_EDUCATIONAL: Educational Games\n" +
"- GAME_MUSIC: Music Games\n" +
"- GAME_PUZZLE: Puzzle Games\n" +
"- GAME_RACING: Racing Games\n" +
"- GAME_ROLE_PLAYING: Role Playing Games\n" +
"- GAME_SIMULATION: Simulation Games\n" +
"- GAME_SPORTS: Sports Games\n" +
"- GAME_STRATEGY: Strategy Games\n" +
"- GAME_TRIVIA: Trivia Games\n" +
"- GAME_WORD: Word Games\n" +
"- FAMILY: Family Games"
),
age: z.enum(['FIVE_UNDER', 'SIX_EIGHT', 'NINE_UP'])
.optional()
.describe("Age range filter (only for FAMILY category). Options: FIVE_UNDER, SIX_EIGHT, NINE_UP"),
num: z.number()
.default(500)
.describe("Number of apps to retrieve (default: 500)"),
lang: z.string()
.default("en")
.describe("Language code for result text (default: en)"),
country: z.string()
.default("us")
.describe("Country code to get results from (default: us)"),
fullDetail: z.boolean()
.default(false)
.describe("Include full app details in results (default: false)")
},
async ({ collection, category, age, num, lang, country, fullDetail }) => {
const results = await gplay.list({
collection,
category,
age,
num,
lang,
country,
fullDetail
});
return { content: [{ type: "text", text: JSON.stringify(results) }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);