google-play-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/.env.example +2 -0
- package/README.md +208 -0
- package/bin/cli.js +111 -0
- package/locales.json +58 -0
- package/package.json +28 -0
- package/requirements.txt +5 -0
- package/server.py +1263 -0
- package/setup_key.py +212 -0
package/.env.example
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Google Play Developer API MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for managing Google Play app deployment and in-app products via the Android Publisher API v3.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### App Deployment
|
|
8
|
+
|
|
9
|
+
- **deploy_internal**: Upload AAB and deploy to internal testing track
|
|
10
|
+
|
|
11
|
+
### In-App Products
|
|
12
|
+
|
|
13
|
+
- **create_inapp_product**: Create or update a single in-app product
|
|
14
|
+
- **batch_create_inapp_products**: Create multiple products at once
|
|
15
|
+
- **activate_inapp_product**: Activate a draft product
|
|
16
|
+
- **batch_activate_inapp_products**: Activate multiple products
|
|
17
|
+
- **deactivate_inapp_product**: Deactivate an active product
|
|
18
|
+
- **list_inapp_products**: List all one-time products
|
|
19
|
+
- **list_subscriptions**: List all subscription products
|
|
20
|
+
|
|
21
|
+
### App Info
|
|
22
|
+
|
|
23
|
+
- **get_app_info**: Get app track and version information
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Python 3.10+
|
|
28
|
+
- Google Cloud service account with Google Play Developer API access
|
|
29
|
+
- App registered in Google Play Console
|
|
30
|
+
|
|
31
|
+
## Installation & Usage
|
|
32
|
+
|
|
33
|
+
You can use this MCP server directly with `npx` without installing it manually.
|
|
34
|
+
|
|
35
|
+
### Quick Start (npx)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Configure your API key
|
|
39
|
+
npx google-play-mcp init-key
|
|
40
|
+
|
|
41
|
+
# For Korean instructions (한국어 안내)
|
|
42
|
+
npx google-play-mcp init-key --lang ko
|
|
43
|
+
|
|
44
|
+
# Start the server
|
|
45
|
+
npx google-play-mcp start
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Installation (Global)
|
|
49
|
+
|
|
50
|
+
If you prefer to install it globally (from npm):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -g google-play-mcp
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Installation (Local Dev)
|
|
57
|
+
|
|
58
|
+
To install from the cloned repository:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install -g .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then you can run:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
google-play-mcp init-key
|
|
68
|
+
google-play-mcp start
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Configuration (Antigravity & Claude Desktop)
|
|
72
|
+
|
|
73
|
+
To use this MCP server, add the following configuration to your MCP client (e.g., `claude_desktop_config.json` or Antigravity settings):
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"google-play": {
|
|
79
|
+
"command": "google-play-mcp",
|
|
80
|
+
"args": ["start"],
|
|
81
|
+
"env": {
|
|
82
|
+
"GOOGLE_PLAY_KEY_FILE": "/absolute/path/to/your-key.json",
|
|
83
|
+
"GOOGLE_PLAY_PACKAGE_NAME": "com.yourcompany.yourapp"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> **Note:** If you haven't run `init-key` or don't have a `.env` file, you can pass environment variables directly in the configuration as shown above. If you have a `.env` file in the working directory, the server will load it automatically.
|
|
91
|
+
|
|
92
|
+
1. Login to npm:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm login
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
1. Publish the package:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm publish
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
The `npm run init-key` script will automatically create a `.env` file with your configuration:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
GOOGLE_PLAY_KEY_FILE=/absolute/path/to/your-key.json
|
|
110
|
+
GOOGLE_PLAY_PACKAGE_NAME=com.yourcompany.yourapp
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Usage
|
|
114
|
+
|
|
115
|
+
To start the server manually:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm start
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Usage with Claude Desktop
|
|
122
|
+
|
|
123
|
+
Add to your `claude_desktop_config.json`:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"mcpServers": {
|
|
128
|
+
"google-play": {
|
|
129
|
+
"command": "npm",
|
|
130
|
+
"args": ["start"],
|
|
131
|
+
"cwd": "/absolute/path/to/google-play-mcp"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Tool Examples
|
|
138
|
+
|
|
139
|
+
### Deploy to Internal Testing
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
Deploy app-release.aab to internal testing with Korean and English release notes
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Create In-App Product
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Create an in-app product:
|
|
149
|
+
- SKU: gems_100
|
|
150
|
+
- Korean: 보석 100개 / 보석 100개를 획득합니다
|
|
151
|
+
- English: 100 Gems / Get 100 gems
|
|
152
|
+
- Price: $0.99 USD
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Batch Create Products
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
[
|
|
159
|
+
{"sku": "gems_12", "title_ko": "보석 12개", "title_en": "12 Gems",
|
|
160
|
+
"description_ko": "보석 12개 획득", "description_en": "Get 12 gems", "price_usd": 0.99},
|
|
161
|
+
{"sku": "gems_66", "title_ko": "보석 66개", "title_en": "66 Gems",
|
|
162
|
+
"description_ko": "보석 66개 획득", "description_en": "Get 66 gems", "price_usd": 4.99}
|
|
163
|
+
]
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Activate Products
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
["gems_12", "gems_66", "gems_136"]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Important Notes
|
|
173
|
+
|
|
174
|
+
### Prerequisites for In-App Products
|
|
175
|
+
|
|
176
|
+
Before creating in-app products, your app must have:
|
|
177
|
+
|
|
178
|
+
1. `com.android.vending.BILLING` permission in `AndroidManifest.xml`
|
|
179
|
+
2. Play Billing Library 6.0.1+ (Flutter: `in_app_purchase` package)
|
|
180
|
+
3. A bundle with these uploaded to Google Play
|
|
181
|
+
|
|
182
|
+
### Draft Apps
|
|
183
|
+
|
|
184
|
+
Apps that have never been published can only use `status: "draft"` for deployments.
|
|
185
|
+
You must manually publish through Google Play Console for the first release.
|
|
186
|
+
|
|
187
|
+
### API Migration
|
|
188
|
+
|
|
189
|
+
This server uses the new `monetization.onetimeproducts` API instead of the deprecated
|
|
190
|
+
`inappproducts` API which returns 403 errors.
|
|
191
|
+
|
|
192
|
+
### Price Conversion
|
|
193
|
+
|
|
194
|
+
USD prices are automatically converted to 170+ regional currencies using
|
|
195
|
+
Google's `convertRegionPrices` API.
|
|
196
|
+
|
|
197
|
+
## Tracks
|
|
198
|
+
|
|
199
|
+
| Track | Description |
|
|
200
|
+
|-------|-------------|
|
|
201
|
+
| `internal` | Internal testing (up to 100 testers) |
|
|
202
|
+
| `alpha` | Closed testing |
|
|
203
|
+
| `beta` | Open testing |
|
|
204
|
+
| `production` | Production release |
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT License
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
// Determine the root directory of the package
|
|
8
|
+
const packageRoot = path.join(__dirname, '..');
|
|
9
|
+
const venvDir = path.join(packageRoot, '.venv');
|
|
10
|
+
const requirementsFile = path.join(packageRoot, 'requirements.txt');
|
|
11
|
+
|
|
12
|
+
// Platform specific paths
|
|
13
|
+
const isWindows = process.platform === 'win32';
|
|
14
|
+
const pythonExecutable = isWindows
|
|
15
|
+
? path.join(venvDir, 'Scripts', 'python.exe')
|
|
16
|
+
: path.join(venvDir, 'bin', 'python');
|
|
17
|
+
|
|
18
|
+
const pipExecutable = isWindows
|
|
19
|
+
? path.join(venvDir, 'Scripts', 'pip.exe')
|
|
20
|
+
: path.join(venvDir, 'bin', 'pip');
|
|
21
|
+
|
|
22
|
+
function runCommand(command, args, options = {}) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const proc = spawn(command, args, {
|
|
25
|
+
stdio: 'inherit',
|
|
26
|
+
cwd: packageRoot,
|
|
27
|
+
...options
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
proc.on('close', (code) => {
|
|
31
|
+
if (code === 0) {
|
|
32
|
+
resolve();
|
|
33
|
+
} else {
|
|
34
|
+
reject(new Error(`Command exited with code ${code}`));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
proc.on('error', (err) => {
|
|
39
|
+
reject(err);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function setupEnvironment() {
|
|
45
|
+
console.log('Initializing Python virtual environment...');
|
|
46
|
+
|
|
47
|
+
// Create venv if it doesn't exist
|
|
48
|
+
if (!fs.existsSync(venvDir)) {
|
|
49
|
+
console.log('Creating .venv...');
|
|
50
|
+
try {
|
|
51
|
+
await runCommand('python3', ['-m', 'venv', '.venv']);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// Try just 'python' if 'python3' fails (e.g. Windows)
|
|
54
|
+
await runCommand('python', ['-m', 'venv', '.venv']);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Install dependencies
|
|
59
|
+
if (fs.existsSync(requirementsFile)) {
|
|
60
|
+
console.log('Installing dependencies...');
|
|
61
|
+
await runCommand(pipExecutable, ['install', '-r', 'requirements.txt']);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function main() {
|
|
66
|
+
const args = process.argv.slice(2);
|
|
67
|
+
const command = args[0];
|
|
68
|
+
|
|
69
|
+
// Check if environment needs setup
|
|
70
|
+
if (!fs.existsSync(pythonExecutable)) {
|
|
71
|
+
try {
|
|
72
|
+
await setupEnvironment();
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('Failed to setup Python environment:', error);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Determine which script to run
|
|
80
|
+
let scriptToRun;
|
|
81
|
+
let pythonArgs = [];
|
|
82
|
+
|
|
83
|
+
if (command === 'init-key') {
|
|
84
|
+
scriptToRun = 'setup_key.py';
|
|
85
|
+
pythonArgs = args.slice(1);
|
|
86
|
+
} else if (command === 'start' || !command) {
|
|
87
|
+
scriptToRun = 'server.py';
|
|
88
|
+
pythonArgs = args.slice(1);
|
|
89
|
+
if (command === 'start') {
|
|
90
|
+
pythonArgs = args.slice(1); // 'start' is consumed
|
|
91
|
+
} else {
|
|
92
|
+
pythonArgs = args; // No command provided, pass all args
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
// Unknown command, maybe it's an arg for server.py?
|
|
96
|
+
// Default to running server.py with these args
|
|
97
|
+
scriptToRun = 'server.py';
|
|
98
|
+
pythonArgs = args;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const scriptPath = path.join(packageRoot, scriptToRun);
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await runCommand(pythonExecutable, [scriptPath, ...pythonArgs]);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Error executing script:', error);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
main();
|
package/locales.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"en": {
|
|
3
|
+
"step1_title": "Step 1: Enable Google Play API",
|
|
4
|
+
"step1_desc": "\n1. Create a new project or select an existing one.\n2. Click **Enable** for **\"Google Play Android Developer API\"**.\n ",
|
|
5
|
+
"step2_title": "Step 2: Create Service Account & Download Key",
|
|
6
|
+
"step2_desc": "\n1. Click **Create Service Account**.\n2. Fill in the suggested details below (Select options to copy).\n3. Click **Create and Continue**.\n4. Click **Done** (Granting Service Account User role is optional).\n5. Click on the **Email** of the newly created account.\n6. Go to the **Keys** tab (top menu).\n7. Click **Add Key** > **Create new key** > **JSON** > **Create**.\n8. The JSON file will download. **Keep this file ready.**\n ",
|
|
7
|
+
"step3_title": "Step 3: Grant Permissions in Play Console",
|
|
8
|
+
"step3_desc": "\n1. Have your **Service Account Email** ready (from Step 2).\n2. Click **Invite new users**.\n3. Paste the service account email.\n4. Grant **Admin** permissions (or at least Release & Financial).\n5. Click **Invite user**.\n ",
|
|
9
|
+
"open_browser": "\nOpen browser to perform this step? (Press Enter to Open) [y/N]",
|
|
10
|
+
"sa_details_title": "Service Account Details",
|
|
11
|
+
"sa_suggested_title": "[bold]Suggested Values (Double-click or drag to select):[/bold]",
|
|
12
|
+
"sa_name_label": "Service Account Name:",
|
|
13
|
+
"sa_id_label": "Service Account ID:",
|
|
14
|
+
"sa_desc_label": "Description:",
|
|
15
|
+
"copy_menu_intro": "\n[dim]You can use the menu below to copy values to your clipboard, or copy them manually from above.[/dim]",
|
|
16
|
+
"copy_menu_prompt": "\n[bold]Copy Actions (Optional):[/bold]\n[1] Copy Name & ID (Formatted Text)\n[2] Copy Description\n[Enter] Skip / Continue to next step",
|
|
17
|
+
"final_config_title": "[bold green]Final Configuration[/bold green]",
|
|
18
|
+
"key_path_prompt": "\n[bold]Drag and drop (or enter path)[/bold] to your downloaded JSON key file",
|
|
19
|
+
"key_file_error": "[bold red]Error:[/bold red] File not found at {key_path}. Please try again.",
|
|
20
|
+
"package_name_prompt": "Enter your [bold]App Package Name[/bold] (e.g. com.example.app)",
|
|
21
|
+
"success_msg": "\n[bold green]Success![/bold green] Configured .env\nKey: {key_path}\nPackage: {package_name}",
|
|
22
|
+
"pyperclip_install": "[yellow]pyperclip not found. Installing...[/yellow]",
|
|
23
|
+
"pyperclip_error": "[red]Failed to import pyperclip after installation.[/red]",
|
|
24
|
+
"copy_manual": "Please copy manually: [bold]{text}[/bold]",
|
|
25
|
+
"copy_install_fail": "[red]Failed to install pyperclip.[/red]",
|
|
26
|
+
"copy_success": "[green]Copied to clipboard: {text}[/green]",
|
|
27
|
+
"copy_fail": "[red]Failed to copy to clipboard: {e}[/red]",
|
|
28
|
+
"press_enter": "Press Enter key inside this terminal after you have completed the steps above..."
|
|
29
|
+
},
|
|
30
|
+
"ko": {
|
|
31
|
+
"step1_title": "1단계: Google Play API 활성화",
|
|
32
|
+
"step1_desc": "\n1. 새 프로젝트를 생성하거나 기존 프로젝트를 선택하세요.\n2. **\"Google Play Android Developer API\"**를 찾아 **사용(Enable)**을 클릭하세요.\n ",
|
|
33
|
+
"step2_title": "2단계: 서비스 계정 생성 및 키 다운로드",
|
|
34
|
+
"step2_desc": "\n1. **서비스 계정 만들기**를 클릭하세요.\n2. 아래 제안된 내용을 복사하여 입력하세요.\n3. **만들기 및 계속**을 클릭하세요.\n4. **완료**를 클릭하세요 (권한 부여는 선택 사항입니다).\n5. 새로 생성된 계정의 **이메일**을 클릭하세요.\n6. **키** 탭(상단 메뉴)으로 이동하세요.\n7. **키 추가** > **새 키 만들기** > **JSON** > **만들기**를 클릭하세요.\n8. JSON 파일이 다운로드됩니다. **이 파일을 잘 보관하세요.**\n ",
|
|
35
|
+
"step3_title": "3단계: Play Console에서 권한 부여",
|
|
36
|
+
"step3_desc": "\n1. **서비스 계정 이메일**을 준비하세요 (2단계에서 생성됨).\n2. **사용자 초대**를 클릭하세요.\n3. 서비스 계정 이메일을 붙여넣으세요.\n4. **관리자** 권한(또는 최소한 출시 및 금융 권한)을 부여하세요.\n5. **사용자 초대**를 클릭하세요.\n ",
|
|
37
|
+
"open_browser": "\n이 단계를 수행하기 위해 브라우저를 여시겠습니까? (엔터를 누르면 열립니다) [y/N]",
|
|
38
|
+
"sa_details_title": "서비스 계정 상세 정보",
|
|
39
|
+
"sa_suggested_title": "[bold]추천 값 (더블 클릭하거나 드래그하여 선택):[/bold]",
|
|
40
|
+
"sa_name_label": "서비스 계정 이름:",
|
|
41
|
+
"sa_id_label": "서비스 계정 ID:",
|
|
42
|
+
"sa_desc_label": "설명:",
|
|
43
|
+
"copy_menu_intro": "\n[dim]아래 메뉴를 사용하여 클립보드에 복사하거나, 위 내용을 수동으로 복사하세요.[/dim]",
|
|
44
|
+
"copy_menu_prompt": "\n[bold]복사 작업 (선택 사항):[/bold]\n[1] 이름 & ID 복사 (서식 포함)\n[2] 설명 복사\n[Enter] 건너뛰기 / 다음 단계로 계속",
|
|
45
|
+
"final_config_title": "[bold green]최종 설정[/bold green]",
|
|
46
|
+
"key_path_prompt": "\n다운로드한 JSON 키 파일을 이곳에 [bold]드래그 앤 드롭[/bold] (또는 경로 입력) 하세요",
|
|
47
|
+
"key_file_error": "[bold red]오류:[/bold red] 파일을 찾을 수 없습니다: {key_path}. 다시 시도해주세요.",
|
|
48
|
+
"package_name_prompt": "[bold]앱 패키지 이름[/bold]을 입력하세요 (예: com.example.app)",
|
|
49
|
+
"success_msg": "\n[bold green]성공![/bold green] .env 설정 완료\n키: {key_path}\n패키지: {package_name}",
|
|
50
|
+
"pyperclip_install": "[yellow]pyperclip을 찾을 수 없습니다. 설치 중...[/yellow]",
|
|
51
|
+
"pyperclip_error": "[red]설치 후 pyperclip을 가져오는데 실패했습니다.[/red]",
|
|
52
|
+
"copy_manual": "수동으로 복사해주세요: [bold]{text}[/bold]",
|
|
53
|
+
"copy_install_fail": "[red]pyperclip 설치에 실패했습니다.[/red]",
|
|
54
|
+
"copy_success": "[green]클립보드에 복사되었습니다: {text}[/green]",
|
|
55
|
+
"copy_fail": "[red]클립보드 복사 실패: {e}[/red]",
|
|
56
|
+
"press_enter": "위 단계를 완료한 후 이 터미널에서 Enter 키를 누르세요..."
|
|
57
|
+
}
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "google-play-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Google Play Developer API",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "node bin/cli.js start",
|
|
7
|
+
"init-key": "node bin/cli.js init-key"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"google-play-mcp": "./bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"server.py",
|
|
14
|
+
"setup_key.py",
|
|
15
|
+
"requirements.txt",
|
|
16
|
+
"bin/cli.js",
|
|
17
|
+
".env.example",
|
|
18
|
+
"locales.json"
|
|
19
|
+
],
|
|
20
|
+
"preferGlobal": true,
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"google-play",
|
|
24
|
+
"python"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|