sensor-tower-mcp-pro 1.2.10
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/README.md +136 -0
- package/bin/sensortower-mcp.js +2 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +100 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +142 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/app-analysis.d.ts +580 -0
- package/dist/tools/app-analysis.d.ts.map +1 -0
- package/dist/tools/app-analysis.js +451 -0
- package/dist/tools/app-analysis.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +19 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/market-analysis.d.ts +391 -0
- package/dist/tools/market-analysis.d.ts.map +1 -0
- package/dist/tools/market-analysis.js +298 -0
- package/dist/tools/market-analysis.js.map +1 -0
- package/dist/tools/search.d.ts +162 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +126 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/store-marketing.d.ts +200 -0
- package/dist/tools/store-marketing.d.ts.map +1 -0
- package/dist/tools/store-marketing.js +170 -0
- package/dist/tools/store-marketing.js.map +1 -0
- package/dist/tools/utilities.d.ts +76 -0
- package/dist/tools/utilities.d.ts.map +1 -0
- package/dist/tools/utilities.js +140 -0
- package/dist/tools/utilities.js.map +1 -0
- package/dist/tools/your-metrics.d.ts +140 -0
- package/dist/tools/your-metrics.d.ts.map +1 -0
- package/dist/tools/your-metrics.js +121 -0
- package/dist/tools/your-metrics.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +64 -0
- package/dist/utils.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Sensor Tower MCP Server (npm)
|
|
2
|
+
|
|
3
|
+
A pure Node.js implementation of the Model Context Protocol server for Sensor Tower APIs. Access mobile app intelligence data directly in MCP-compatible tools like Cursor and Claude Desktop.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **App Intelligence** - Metadata, rankings, downloads, revenue estimates
|
|
8
|
+
- **Search & Discovery** - Find apps and publishers by name or description
|
|
9
|
+
- **Market Analysis** - Category rankings, featured apps, competitor insights
|
|
10
|
+
- **Multi-Token Failover** - Automatic switch to backup tokens when quota exhausted
|
|
11
|
+
- **No Python Required** - Pure Node.js implementation
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run directly with npx (recommended)
|
|
17
|
+
npx sensortower-mcp
|
|
18
|
+
|
|
19
|
+
# Or install globally
|
|
20
|
+
npm install -g sensortower-mcp
|
|
21
|
+
sensortower-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- **Node.js 18+**
|
|
27
|
+
- Sensor Tower API token
|
|
28
|
+
|
|
29
|
+
## MCP Configuration
|
|
30
|
+
|
|
31
|
+
### Cursor / Claude Desktop
|
|
32
|
+
|
|
33
|
+
Add to your MCP settings:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"sensortower": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "sensortower-mcp"],
|
|
41
|
+
"env": {
|
|
42
|
+
"SENSOR_TOWER_API_TOKEN": "primary_token",
|
|
43
|
+
"SENSOR_TOWER_API_TOKEN_BACKUP": "backup_token"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Multi-Token Failover
|
|
51
|
+
|
|
52
|
+
When a token's API quota is exhausted (429/403 error), the server automatically switches to the next available backup token. This ensures uninterrupted API access.
|
|
53
|
+
|
|
54
|
+
### Environment Variables
|
|
55
|
+
|
|
56
|
+
| Variable | Description |
|
|
57
|
+
|----------|-------------|
|
|
58
|
+
| `SENSOR_TOWER_API_TOKEN` | Primary API token (required) |
|
|
59
|
+
| `SENSOR_TOWER_API_TOKEN_BACKUP` | First backup token |
|
|
60
|
+
| `SENSOR_TOWER_API_TOKEN_2` | Second backup token |
|
|
61
|
+
| `SENSOR_TOWER_API_TOKEN_3` | Third backup token (up to 10) |
|
|
62
|
+
|
|
63
|
+
### Example with Multiple Tokens
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
SENSOR_TOWER_API_TOKEN=token1 \
|
|
67
|
+
SENSOR_TOWER_API_TOKEN_BACKUP=token2 \
|
|
68
|
+
SENSOR_TOWER_API_TOKEN_2=token3 \
|
|
69
|
+
npx sensortower-mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API Token
|
|
73
|
+
|
|
74
|
+
Get your API token from [Sensor Tower Account Settings](https://app.sensortower.com/users/edit/api-settings).
|
|
75
|
+
|
|
76
|
+
## Available Tools
|
|
77
|
+
|
|
78
|
+
| Tool | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `get_app_metadata` | App details, ratings, categories |
|
|
81
|
+
| `search_entities` | Search apps and publishers |
|
|
82
|
+
| `get_category_rankings` | Top apps by category |
|
|
83
|
+
| `get_download_estimates` | Download trends and estimates |
|
|
84
|
+
| `get_revenue_estimates` | Revenue data and forecasts |
|
|
85
|
+
| `get_creatives` | Advertising creatives |
|
|
86
|
+
| `get_usage_active_users` | Active users data |
|
|
87
|
+
| `top_in_app_purchases` | Top in-app purchases |
|
|
88
|
+
| `version_history` | App version history |
|
|
89
|
+
| `get_country_codes` | Available country codes |
|
|
90
|
+
| `get_category_ids` | Platform category IDs |
|
|
91
|
+
|
|
92
|
+
## Command Line Options
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx sensortower-mcp [OPTIONS]
|
|
96
|
+
|
|
97
|
+
Options:
|
|
98
|
+
--transport <type> Transport type: stdio (default) or http
|
|
99
|
+
--port <port> HTTP port (default: 8666, only for http transport)
|
|
100
|
+
--token <token> API token (or set SENSOR_TOWER_API_TOKEN env var)
|
|
101
|
+
--backup-token <tok> Backup API token for failover
|
|
102
|
+
--help, -h Show help message
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Examples
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Run with stdio transport (for MCP clients)
|
|
109
|
+
SENSOR_TOWER_API_TOKEN=your_token npx sensortower-mcp
|
|
110
|
+
|
|
111
|
+
# Run with HTTP transport
|
|
112
|
+
npx sensortower-mcp --transport http --port 8666
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Install dependencies
|
|
119
|
+
npm install
|
|
120
|
+
|
|
121
|
+
# Build
|
|
122
|
+
npm run build
|
|
123
|
+
|
|
124
|
+
# Run locally
|
|
125
|
+
npm start
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Links
|
|
129
|
+
|
|
130
|
+
- [GitHub Repository](https://github.com/sensortower/sensortower-mcp)
|
|
131
|
+
- [Sensor Tower API Docs](https://docs.sensortower.com/)
|
|
132
|
+
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for Sensor Tower API
|
|
3
|
+
*/
|
|
4
|
+
export declare class SensorTowerClient {
|
|
5
|
+
private tokens;
|
|
6
|
+
private currentTokenIndex;
|
|
7
|
+
constructor(tokens: string[]);
|
|
8
|
+
private getAuthToken;
|
|
9
|
+
private switchToBackupToken;
|
|
10
|
+
getTokenStatus(): {
|
|
11
|
+
current: number;
|
|
12
|
+
total: number;
|
|
13
|
+
};
|
|
14
|
+
makeRequest<T>(endpoint: string, params: Record<string, any>): Promise<T>;
|
|
15
|
+
private sleep;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,iBAAiB,CAAa;gBAE1B,MAAM,EAAE,MAAM,EAAE;IAO5B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,mBAAmB;IAU3B,cAAc,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAO9C,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsE/E,OAAO,CAAC,KAAK;CAGd"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP client for Sensor Tower API
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SensorTowerClient = void 0;
|
|
7
|
+
const config_1 = require("./config");
|
|
8
|
+
class SensorTowerClient {
|
|
9
|
+
tokens;
|
|
10
|
+
currentTokenIndex = 0;
|
|
11
|
+
constructor(tokens) {
|
|
12
|
+
if (tokens.length === 0) {
|
|
13
|
+
throw new Error('At least one API token is required');
|
|
14
|
+
}
|
|
15
|
+
this.tokens = tokens;
|
|
16
|
+
}
|
|
17
|
+
getAuthToken() {
|
|
18
|
+
return this.tokens[this.currentTokenIndex];
|
|
19
|
+
}
|
|
20
|
+
switchToBackupToken() {
|
|
21
|
+
if (this.currentTokenIndex < this.tokens.length - 1) {
|
|
22
|
+
this.currentTokenIndex++;
|
|
23
|
+
console.error(`⚠️ Token #${this.currentTokenIndex} quota exceeded, switching to token #${this.currentTokenIndex + 1}`);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
console.error(`❌ All ${this.tokens.length} tokens exhausted`);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
getTokenStatus() {
|
|
30
|
+
return {
|
|
31
|
+
current: this.currentTokenIndex + 1,
|
|
32
|
+
total: this.tokens.length,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async makeRequest(endpoint, params) {
|
|
36
|
+
params.auth_token = this.getAuthToken();
|
|
37
|
+
let backoffMs = 500;
|
|
38
|
+
const maxAttempts = 5;
|
|
39
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL(endpoint, config_1.API_BASE_URL);
|
|
42
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
43
|
+
if (value !== undefined && value !== null) {
|
|
44
|
+
url.searchParams.append(key, String(value));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const response = await fetch(url.toString(), {
|
|
48
|
+
method: 'GET',
|
|
49
|
+
headers: {
|
|
50
|
+
'Accept': 'application/json',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const statusCode = response.status;
|
|
55
|
+
// Check for quota/rate limit errors
|
|
56
|
+
let isQuotaError = statusCode === 429;
|
|
57
|
+
if (statusCode === 403) {
|
|
58
|
+
try {
|
|
59
|
+
const errorBody = await response.text();
|
|
60
|
+
const errorMessage = errorBody.toLowerCase();
|
|
61
|
+
isQuotaError = ['quota', 'limit', 'exceeded', 'rate'].some(keyword => errorMessage.includes(keyword));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Ignore parse errors
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Try backup token on quota errors
|
|
68
|
+
if (isQuotaError && this.switchToBackupToken()) {
|
|
69
|
+
params.auth_token = this.getAuthToken();
|
|
70
|
+
console.error('Retrying request with backup token...');
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// Retry on server errors
|
|
74
|
+
if ([429, 500, 502, 503, 504].includes(statusCode) && attempt < maxAttempts - 1) {
|
|
75
|
+
await this.sleep(backoffMs);
|
|
76
|
+
backoffMs = Math.min(backoffMs * 2, 8000);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`HTTP ${statusCode}: ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
return await response.json();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (attempt < maxAttempts - 1 && error instanceof TypeError) {
|
|
85
|
+
// Network error, retry
|
|
86
|
+
await this.sleep(backoffMs);
|
|
87
|
+
backoffMs = Math.min(backoffMs * 2, 8000);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
throw new Error('Max retry attempts exceeded');
|
|
94
|
+
}
|
|
95
|
+
sleep(ms) {
|
|
96
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.SensorTowerClient = SensorTowerClient;
|
|
100
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for Sensor Tower MCP Server
|
|
3
|
+
*/
|
|
4
|
+
export declare const API_BASE_URL: string;
|
|
5
|
+
export interface Config {
|
|
6
|
+
tokens: string[];
|
|
7
|
+
transport: 'stdio' | 'http';
|
|
8
|
+
port: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function getConfig(): Config;
|
|
11
|
+
export declare function parseArgs(): Config;
|
|
12
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,YAAY,QAA4D,CAAC;AAEtF,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,SAAS,IAAI,MAAM,CAclC;AA2BD,wBAAgB,SAAS,IAAI,MAAM,CA6ClC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for Sensor Tower MCP Server
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.API_BASE_URL = void 0;
|
|
7
|
+
exports.getConfig = getConfig;
|
|
8
|
+
exports.parseArgs = parseArgs;
|
|
9
|
+
exports.API_BASE_URL = process.env.API_BASE_URL || 'https://api.sensortower.com';
|
|
10
|
+
function getConfig() {
|
|
11
|
+
const tokens = collectTokensFromEnv();
|
|
12
|
+
if (tokens.length === 0) {
|
|
13
|
+
console.error('Error: SENSOR_TOWER_API_TOKEN environment variable is required');
|
|
14
|
+
console.error('Get your API token from: https://app.sensortower.com/users/edit/api-settings');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
tokens,
|
|
19
|
+
transport: process.env.TRANSPORT || 'stdio',
|
|
20
|
+
port: parseInt(process.env.PORT || '8666', 10),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Collect all tokens from environment variables
|
|
25
|
+
* Supports: SENSOR_TOWER_API_TOKEN, SENSOR_TOWER_API_TOKEN_BACKUP,
|
|
26
|
+
* SENSOR_TOWER_API_TOKEN_2, SENSOR_TOWER_API_TOKEN_3, etc.
|
|
27
|
+
*/
|
|
28
|
+
function collectTokensFromEnv() {
|
|
29
|
+
const tokens = [];
|
|
30
|
+
// Primary token
|
|
31
|
+
const primary = process.env.SENSOR_TOWER_API_TOKEN;
|
|
32
|
+
if (primary)
|
|
33
|
+
tokens.push(primary);
|
|
34
|
+
// Backup token (legacy support)
|
|
35
|
+
const backup = process.env.SENSOR_TOWER_API_TOKEN_BACKUP;
|
|
36
|
+
if (backup)
|
|
37
|
+
tokens.push(backup);
|
|
38
|
+
// Additional numbered tokens (2, 3, 4, ...)
|
|
39
|
+
for (let i = 2; i <= 10; i++) {
|
|
40
|
+
const token = process.env[`SENSOR_TOWER_API_TOKEN_${i}`];
|
|
41
|
+
if (token)
|
|
42
|
+
tokens.push(token);
|
|
43
|
+
}
|
|
44
|
+
return tokens;
|
|
45
|
+
}
|
|
46
|
+
function parseArgs() {
|
|
47
|
+
const args = process.argv.slice(2);
|
|
48
|
+
const tokens = [];
|
|
49
|
+
let transport = 'stdio';
|
|
50
|
+
let port = 8666;
|
|
51
|
+
// First collect from environment
|
|
52
|
+
tokens.push(...collectTokensFromEnv());
|
|
53
|
+
for (let i = 0; i < args.length; i++) {
|
|
54
|
+
switch (args[i]) {
|
|
55
|
+
case '--token':
|
|
56
|
+
// Primary token from CLI overrides env
|
|
57
|
+
const cliToken = args[++i];
|
|
58
|
+
if (cliToken && !tokens.includes(cliToken)) {
|
|
59
|
+
tokens.unshift(cliToken); // Add to front as primary
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case '--backup-token':
|
|
63
|
+
// Add backup token
|
|
64
|
+
const backupToken = args[++i];
|
|
65
|
+
if (backupToken && !tokens.includes(backupToken)) {
|
|
66
|
+
tokens.push(backupToken);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
case '--transport':
|
|
70
|
+
transport = args[++i];
|
|
71
|
+
break;
|
|
72
|
+
case '--port':
|
|
73
|
+
port = parseInt(args[++i] || '8666', 10);
|
|
74
|
+
break;
|
|
75
|
+
case '--help':
|
|
76
|
+
case '-h':
|
|
77
|
+
printHelp();
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (tokens.length === 0) {
|
|
82
|
+
console.error('Error: SENSOR_TOWER_API_TOKEN environment variable or --token argument is required');
|
|
83
|
+
console.error('Get your API token from: https://app.sensortower.com/users/edit/api-settings');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
return { tokens, transport, port };
|
|
87
|
+
}
|
|
88
|
+
function printHelp() {
|
|
89
|
+
console.log(`
|
|
90
|
+
Sensor Tower MCP Server
|
|
91
|
+
|
|
92
|
+
A Model Context Protocol server for accessing Sensor Tower's mobile app intelligence APIs.
|
|
93
|
+
|
|
94
|
+
USAGE:
|
|
95
|
+
npx sensortower-mcp [OPTIONS]
|
|
96
|
+
|
|
97
|
+
OPTIONS:
|
|
98
|
+
--transport <type> Transport type: stdio (default) or http
|
|
99
|
+
--port <port> HTTP port (default: 8666, only for http transport)
|
|
100
|
+
--token <token> Primary API token (or set SENSOR_TOWER_API_TOKEN env var)
|
|
101
|
+
--backup-token <tok> Backup API token for failover (can use multiple times)
|
|
102
|
+
--help, -h Show this help message
|
|
103
|
+
|
|
104
|
+
ENVIRONMENT VARIABLES:
|
|
105
|
+
SENSOR_TOWER_API_TOKEN Primary API token
|
|
106
|
+
SENSOR_TOWER_API_TOKEN_BACKUP First backup token
|
|
107
|
+
SENSOR_TOWER_API_TOKEN_2 Second backup token
|
|
108
|
+
SENSOR_TOWER_API_TOKEN_3 Third backup token (up to 10)
|
|
109
|
+
|
|
110
|
+
TOKEN FAILOVER:
|
|
111
|
+
When a token's quota is exhausted (429/403 error), the server automatically
|
|
112
|
+
switches to the next available backup token. This ensures uninterrupted
|
|
113
|
+
API access across multiple tokens.
|
|
114
|
+
|
|
115
|
+
EXAMPLES:
|
|
116
|
+
# Run with single token
|
|
117
|
+
SENSOR_TOWER_API_TOKEN=token1 npx sensortower-mcp
|
|
118
|
+
|
|
119
|
+
# Run with multiple backup tokens
|
|
120
|
+
SENSOR_TOWER_API_TOKEN=token1 \\
|
|
121
|
+
SENSOR_TOWER_API_TOKEN_BACKUP=token2 \\
|
|
122
|
+
SENSOR_TOWER_API_TOKEN_2=token3 \\
|
|
123
|
+
npx sensortower-mcp
|
|
124
|
+
|
|
125
|
+
MCP CONFIGURATION (Cursor/Claude Desktop):
|
|
126
|
+
{
|
|
127
|
+
"mcpServers": {
|
|
128
|
+
"sensortower": {
|
|
129
|
+
"command": "npx",
|
|
130
|
+
"args": ["-y", "sensortower-mcp"],
|
|
131
|
+
"env": {
|
|
132
|
+
"SENSOR_TOWER_API_TOKEN": "primary_token",
|
|
133
|
+
"SENSOR_TOWER_API_TOKEN_BACKUP": "backup_token"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
For more information: https://github.com/sensortower/sensortower-mcp
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;GAEG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Sensor Tower MCP Server - Main entry point
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
const client_1 = require("./client");
|
|
12
|
+
const tools_1 = require("./tools");
|
|
13
|
+
async function main() {
|
|
14
|
+
const config = (0, config_1.parseArgs)();
|
|
15
|
+
console.error('🚀 Starting Sensor Tower MCP Server...');
|
|
16
|
+
console.error(`📡 Transport: ${config.transport}`);
|
|
17
|
+
console.error(`🔑 API Tokens configured: ${config.tokens.length} (auto-failover enabled)`);
|
|
18
|
+
// Create API client with all tokens
|
|
19
|
+
const client = new client_1.SensorTowerClient(config.tokens);
|
|
20
|
+
// Register all tools
|
|
21
|
+
const allTools = {
|
|
22
|
+
...(0, tools_1.registerAppAnalysisTools)(client),
|
|
23
|
+
...(0, tools_1.registerSearchTools)(client),
|
|
24
|
+
...(0, tools_1.registerUtilityTools)(),
|
|
25
|
+
...(0, tools_1.registerMarketAnalysisTools)(client),
|
|
26
|
+
...(0, tools_1.registerStoreMarketingTools)(client),
|
|
27
|
+
...(0, tools_1.registerYourMetricsTools)(client),
|
|
28
|
+
};
|
|
29
|
+
// Create MCP server
|
|
30
|
+
const server = new index_js_1.Server({
|
|
31
|
+
name: 'sensortower-mcp',
|
|
32
|
+
version: '1.2.10',
|
|
33
|
+
}, {
|
|
34
|
+
capabilities: {
|
|
35
|
+
tools: {},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// Handle list tools request
|
|
39
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
40
|
+
return {
|
|
41
|
+
tools: Object.entries(allTools).map(([name, tool]) => ({
|
|
42
|
+
name,
|
|
43
|
+
description: tool.description,
|
|
44
|
+
inputSchema: tool.inputSchema,
|
|
45
|
+
})),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
// Handle call tool request
|
|
49
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
50
|
+
const { name, arguments: args } = request.params;
|
|
51
|
+
const tool = allTools[name];
|
|
52
|
+
if (!tool) {
|
|
53
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const result = await tool.handler(args || {});
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: JSON.stringify(result, null, 2),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `Error: ${message}`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// Start server with stdio transport
|
|
80
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
81
|
+
await server.connect(transport);
|
|
82
|
+
console.error('Sensor Tower MCP Server running on stdio');
|
|
83
|
+
}
|
|
84
|
+
main().catch((error) => {
|
|
85
|
+
console.error('Fatal error:', error);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|