Skip to main content
Glama

gp_permissions

Retrieve Google Play app permissions by providing the app ID, with options to specify language, country, and format for permission details.

Instructions

[Google Play] Get app permissions

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
appIdYesGoogle Play app ID
langNoLanguage code (default: en)en
countryNoTwo-letter country code (default: us)us
shortNoIf true, return only permission names (default: false)

Implementation Reference

  • Main handler function for 'gp_permissions' tool. Fetches the app's permissions page HTML using buildPermissionsUrl and parsePermissions to extract permissions data.
    async function handleGPPermissions(args) { try { const { appId, lang = 'en', country = 'us', short = false } = args; if (!appId) { throw new Error('appId is required'); } const url = buildPermissionsUrl({ appId, lang, country }); const html = await fetchText(url); const permissions = parsePermissions(html, short); return { content: [ { type: 'text', text: JSON.stringify({ appId, permissions, count: permissions.length, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: JSON.stringify({ error: error.message }, null, 2), }, ], isError: true, }; } }
  • Input schema definition for the gp_permissions tool in the tools list.
    { name: 'gp_permissions', description: '[Google Play] Get app permissions', inputSchema: { type: 'object', properties: { appId: { type: 'string', description: 'Google Play app ID', }, lang: { type: 'string', description: 'Language code (default: en)', default: 'en', }, country: { type: 'string', description: 'Two-letter country code (default: us)', default: 'us', }, short: { type: 'boolean', description: 'If true, return only permission names (default: false)', default: false, }, }, required: ['appId'], }, },
  • Tool dispatch/registration in the CallToolRequestSchema switch statement.
    case 'gp_permissions': return await handleGPPermissions(args);
  • Helper function that parses the permissions from Google Play app HTML using multiple regex-based extraction strategies.
    export function parsePermissions(html, short = false) { if (!html || typeof html !== 'string') { return []; } const permissions = []; const seenPermissions = new Set(); try { // Strategy 1: Look for permissions section in HTML const permissionsSectionPatterns = [ /<div[^>]*class=["'][^"']*permissions["'][^>]*>([\s\S]*?)<\/div>/i, /<div[^>]*id=["']permissions["'][^>]*>([\s\S]*?)<\/div>/i, /<section[^>]*class=["'][^"']*permissions["'][^>]*>([\s\S]*?)<\/section>/i, /<div[^>]*data-permissions[^>]*>([\s\S]*?)<\/div>/i, ]; for (const pattern of permissionsSectionPatterns) { const sectionMatch = html.match(pattern); if (sectionMatch) { const sectionHtml = sectionMatch[1]; // Extract permission items with multiple patterns const permissionItemPatterns = [ /<div[^>]*class=["'][^"']*permission["'][^>]*>([\s\S]*?)<\/div>/gi, /<li[^>]*class=["'][^"']*permission["'][^>]*>([\s\S]*?)<\/li>/gi, /<div[^>]*data-permission[^>]*>([\s\S]*?)<\/div>/gi, ]; for (const itemPattern of permissionItemPatterns) { const permissionMatches = sectionHtml.matchAll(itemPattern); for (const match of permissionMatches) { const permHtml = match[1]; // Extract permission name with multiple patterns const namePatterns = [ /<div[^>]*class=["'][^"']*permission-name["'][^>]*>([^<]+)<\/div>/i, /<span[^>]*class=["'][^"']*permission-name["'][^>]*>([^<]+)<\/span>/i, /<div[^>]*class=["'][^"']*title["'][^>]*>([^<]+)<\/div>/i, /<span[^>]*>([^<]+)<\/span>/i, /<p[^>]*>([^<]+)<\/p>/i, ]; let permissionName = null; for (const namePattern of namePatterns) { const nameMatch = permHtml.match(namePattern); if (nameMatch) { permissionName = nameMatch[1].trim(); break; } } // Extract permission type/category with multiple patterns const typePatterns = [ /<div[^>]*class=["'][^"']*permission-type["'][^>]*>([^<]+)<\/div>/i, /<span[^>]*class=["'][^"']*permission-type["'][^>]*>([^<]+)<\/span>/i, /<div[^>]*class=["'][^"']*category["'][^>]*>([^<]+)<\/div>/i, /data-type=["']([^"']+)["']/i, ]; let type = ''; for (const typePattern of typePatterns) { const typeMatch = permHtml.match(typePattern); if (typeMatch) { type = typeMatch[1].trim(); break; } } if (permissionName && !seenPermissions.has(permissionName.toLowerCase())) { seenPermissions.add(permissionName.toLowerCase()); if (short) { permissions.push(permissionName); } else { permissions.push({ permission: permissionName, type: type, }); } } } } } } // Strategy 2: Extract from script tags with JSON data const scriptMatches = html.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/gi); for (const match of scriptMatches) { const scriptContent = match[1]; if (scriptContent.includes('permission') || scriptContent.includes('PERMISSION') || scriptContent.includes('uses-permission')) { // Try multiple JSON patterns const jsonPatterns = [ /permissions["']?\s*:\s*\[([\s\S]*?)\]/i, /"permissions"["']?\s*:\s*\[([\s\S]*?)\]/i, /permissionList["']?\s*:\s*\[([\s\S]*?)\]/i, /usesPermissions["']?\s*:\s*\[([\s\S]*?)\]/i, ]; for (const pattern of jsonPatterns) { const permArrayMatch = scriptContent.match(pattern); if (permArrayMatch) { const permData = permArrayMatch[1]; // Try to extract permission names // Pattern 1: Array of strings const stringMatches = permData.matchAll(/"([^"]+)"/g); for (const stringMatch of stringMatches) { const permName = stringMatch[1].trim(); if (permName && permName.length > 3 && !seenPermissions.has(permName.toLowerCase())) { seenPermissions.add(permName.toLowerCase()); if (short) { permissions.push(permName); } else { permissions.push({ permission: permName, type: '', }); } } } // Pattern 2: Array of objects try { const jsonStr = '[' + permData + ']'; const jsonData = JSON.parse(jsonStr); if (Array.isArray(jsonData)) { jsonData.forEach(item => { if (typeof item === 'string') { if (!seenPermissions.has(item.toLowerCase())) { seenPermissions.add(item.toLowerCase()); if (short) { permissions.push(item); } else { permissions.push({ permission: item, type: '', }); } } } else if (item && typeof item === 'object') { const permName = item.name || item.permission || item.label || item.title; const permType = item.type || item.category || ''; if (permName && !seenPermissions.has(permName.toLowerCase())) { seenPermissions.add(permName.toLowerCase()); if (short) { permissions.push(permName); } else { permissions.push({ permission: permName, type: permType, }); } } } }); } } catch (e) { // Not valid JSON, continue } } } } } // Strategy 3: Extract from meta tags or data attributes const metaPermissionMatches = html.matchAll(/<meta[^>]*name=["']permission["'][^>]*content=["']([^"']+)["']/gi); for (const metaMatch of metaPermissionMatches) { const permName = metaMatch[1].trim(); if (permName && !seenPermissions.has(permName.toLowerCase())) { seenPermissions.add(permName.toLowerCase()); if (short) { permissions.push(permName); } else { permissions.push({ permission: permName, type: '', }); } } } // Strategy 4: Look for common permission patterns in text // This is a fallback for when structured data isn't available const commonPermissionPatterns = [ /(?:permission|allows?)\s+(?:the\s+)?(?:app\s+)?(?:to\s+)?(?:access|read|write|modify|delete|use|send|receive|view|get|set|manage|control|change|enable|disable)\s+([^.,!?]+)/gi, ]; // Only use this as last resort if we have very few permissions if (permissions.length < 3) { for (const pattern of commonPermissionPatterns) { const matches = html.matchAll(pattern); for (const match of matches) { const permText = match[1].trim(); if (permText && permText.length > 5 && permText.length < 100 && !seenPermissions.has(permText.toLowerCase())) { seenPermissions.add(permText.toLowerCase()); if (short) { permissions.push(permText); } else { permissions.push({ permission: permText, type: '', }); } } } } } return permissions; } catch (error) { console.error('Error parsing Google Play permissions:', error); return []; } }
  • Helper function that builds the URL for the Google Play app permissions/details page.
    export function buildPermissionsUrl(params) { const { appId, lang = 'en', country = 'us' } = params; if (!appId) { throw new Error('appId is required'); } return `${GOOGLE_PLAY_BASE}/store/apps/details?id=${appId}&gl=${country}&hl=${lang}`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MiguelAlvRed/mobile-store-scraper-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server