storelift-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/README.md +75 -0
- package/index.mjs +133 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# storelift-mcp
|
|
2
|
+
|
|
3
|
+
App Store & Google Play ranking data as an MCP server. Ask your assistant which
|
|
4
|
+
keywords you dropped on, who outranks you, and whether AI assistants mention
|
|
5
|
+
your app at all — without opening a dashboard.
|
|
6
|
+
|
|
7
|
+
Backed by [Storelift](https://storelift.vomcore.com), which measures keyword
|
|
8
|
+
ranks, rivals and AI visibility nightly. **No store credentials required** —
|
|
9
|
+
ranks are read from the public storefront.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- A **Studio** plan (the Public API is enabled on that plan)
|
|
14
|
+
- An API key: Storelift → Settings → API keys → **Generate key**
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
Claude Code:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
claude mcp add storelift -e STORELIFT_API_KEY=sl_live_... -- npx -y storelift-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Claude Desktop (`claude_desktop_config.json`):
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"storelift": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["-y", "storelift-mcp"],
|
|
32
|
+
"env": { "STORELIFT_API_KEY": "sl_live_..." }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
| Tool | Returns |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `list_apps` | tracked apps (id, name, countries, keywords) |
|
|
43
|
+
| `get_keywords` | keyword ranks for one app in one country |
|
|
44
|
+
| `get_rivals` | apps ranking **above** you, with their rank and yours |
|
|
45
|
+
| `get_ai_visibility` | whether assistants name your app, per engine (Claude / ChatGPT / Gemini) |
|
|
46
|
+
| `get_history` | rank history, `[day, rank]` points |
|
|
47
|
+
|
|
48
|
+
## Reading the data
|
|
49
|
+
|
|
50
|
+
Keyword results carry **three distinct states**, and they are not the same thing:
|
|
51
|
+
|
|
52
|
+
- `measured: false` — the query **could not be measured**
|
|
53
|
+
- `rank: null` (with `measured: true`) — measured, but **absent** from the top results
|
|
54
|
+
- `rank: <number>` — the rank
|
|
55
|
+
|
|
56
|
+
Counting an unmeasured day as zero produces a false chart. The tool descriptions
|
|
57
|
+
repeat this so the model does not flatten the three into one.
|
|
58
|
+
|
|
59
|
+
AI visibility is an **observation, not a ranking**: a model's knowledge is frozen
|
|
60
|
+
at a date and the answer is not identical every time. Read the trend, not a
|
|
61
|
+
single measurement.
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
| Variable | Default | Purpose |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `STORELIFT_API_KEY` | — | required; the server exits with a message if unset |
|
|
68
|
+
| `STORELIFT_API` | `https://storelift.vomcore.com` | override the API base |
|
|
69
|
+
|
|
70
|
+
No dependencies — a single file speaking JSON-RPC over stdio. Runs with
|
|
71
|
+
`node index.mjs` just as well as through `npx`.
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
MIT
|
package/index.mjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Storelift MCP sunucusu.
|
|
3
|
+
//
|
|
4
|
+
// NE İŞE YARIYOR: Claude (ya da MCP konuşan başka bir istemci) uygulamanın
|
|
5
|
+
// sıralarını, rakiplerini ve AI görünürlüğünü DOĞRUDAN okuyabiliyor.
|
|
6
|
+
// "Kelimece'nin TR'de düşen kelimeleri hangileri" diye sorup cevabı panoya
|
|
7
|
+
// bakmadan alıyorsun.
|
|
8
|
+
//
|
|
9
|
+
// KURULUM (Claude Desktop → claude_desktop_config.json):
|
|
10
|
+
// {
|
|
11
|
+
// "mcpServers": {
|
|
12
|
+
// "storelift": {
|
|
13
|
+
// "command": "npx",
|
|
14
|
+
// "args": ["-y", "storelift-mcp"],
|
|
15
|
+
// "env": { "STORELIFT_API_KEY": "sl_live_..." }
|
|
16
|
+
// }
|
|
17
|
+
// }
|
|
18
|
+
// }
|
|
19
|
+
//
|
|
20
|
+
// BAĞIMLILIK YOK: stdio üzerinden JSON-RPC elle konuşuluyor. Tek dosya, npm
|
|
21
|
+
// kurulumu gerektirmeden `node index.mjs` ile de çalışır.
|
|
22
|
+
|
|
23
|
+
const BASE = process.env.STORELIFT_API || "https://storelift.vomcore.com";
|
|
24
|
+
const KEY = process.env.STORELIFT_API_KEY;
|
|
25
|
+
|
|
26
|
+
if (!KEY) {
|
|
27
|
+
console.error("STORELIFT_API_KEY tanımlı değil. Ayarlar → API anahtarları'ndan bir anahtar üret.");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function api(path) {
|
|
32
|
+
const res = await fetch(BASE + path, { headers: { "x-api-key": KEY } });
|
|
33
|
+
const body = await res.json().catch(() => ({}));
|
|
34
|
+
if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`);
|
|
35
|
+
return body;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const TOOLS = [
|
|
39
|
+
{
|
|
40
|
+
name: "list_apps",
|
|
41
|
+
description: "Storelift hesabındaki izlenen uygulamaları listeler (id, ad, ülkeler, kelimeler).",
|
|
42
|
+
inputSchema: { type: "object", properties: {} },
|
|
43
|
+
run: () => api("/v1/apps"),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "get_keywords",
|
|
47
|
+
description:
|
|
48
|
+
"Bir uygulamanın bir ülkedeki kelime sıralarını verir. ÜÇ DURUM AYRIDIR: measured=false ölçülemedi, rank=null ölçüldü ama ilk sonuçlarda yok, rank=sayı sıra. Bunları birbirine karıştırma.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
appId: { type: "string", description: "list_apps'ten gelen uygulama kimliği" },
|
|
53
|
+
country: { type: "string", description: "ülke kodu, ör. tr" },
|
|
54
|
+
platform: { type: "string", enum: ["ios", "android"] },
|
|
55
|
+
},
|
|
56
|
+
required: ["appId"],
|
|
57
|
+
},
|
|
58
|
+
run: (a) => api(`/v1/apps/${a.appId}/keywords?country=${a.country || ""}&platform=${a.platform || "ios"}`),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "get_rivals",
|
|
62
|
+
description:
|
|
63
|
+
"Aramada uygulamanın ÜSTÜNDE çıkan uygulamalar. Her rakip için hangi kelimede kaçıncı olduğu ve bizim sıramız birlikte döner (ourRank null ise o kelimede hiç görünmüyoruz).",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: { appId: { type: "string" }, country: { type: "string" }, platform: { type: "string", enum: ["ios", "android"] } },
|
|
67
|
+
required: ["appId"],
|
|
68
|
+
},
|
|
69
|
+
run: (a) => api(`/v1/apps/${a.appId}/rivals?country=${a.country || ""}&platform=${a.platform || "ios"}`),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "get_ai_visibility",
|
|
73
|
+
description:
|
|
74
|
+
"Asistanlara sorulan doğal sorularda uygulamanın anılıp anılmadığı — motor başına (Claude/ChatGPT/Gemini). Bu bir sıralama değil gözlemdir; tek ölçüm kanıt değildir, eğilime bak.",
|
|
75
|
+
inputSchema: { type: "object", properties: { appId: { type: "string" }, country: { type: "string" } }, required: ["appId"] },
|
|
76
|
+
run: (a) => api(`/v1/apps/${a.appId}/ai?country=${a.country || ""}`),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "get_history",
|
|
80
|
+
description: "Bir ülkedeki kelime sıra geçmişi. Seri noktaları [gün, sıra] biçiminde; sıra null ise o gün ilk sonuçlarda yoktuk.",
|
|
81
|
+
inputSchema: { type: "object", properties: { appId: { type: "string" }, country: { type: "string" } }, required: ["appId"] },
|
|
82
|
+
run: (a) => api(`/v1/apps/${a.appId}/history?country=${a.country || ""}`),
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const send = (msg) => process.stdout.write(JSON.stringify(msg) + "\n");
|
|
87
|
+
const ok = (id, result) => send({ jsonrpc: "2.0", id, result });
|
|
88
|
+
const err = (id, message) => send({ jsonrpc: "2.0", id, error: { code: -32000, message } });
|
|
89
|
+
|
|
90
|
+
async function handle(req) {
|
|
91
|
+
const { id, method, params } = req;
|
|
92
|
+
if (method === "initialize") {
|
|
93
|
+
return ok(id, {
|
|
94
|
+
protocolVersion: "2024-11-05",
|
|
95
|
+
capabilities: { tools: {} },
|
|
96
|
+
serverInfo: { name: "storelift", version: "1.0.0" },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (method === "notifications/initialized") return; // bildirim, yanıt beklemez
|
|
100
|
+
if (method === "tools/list") {
|
|
101
|
+
return ok(id, { tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })) });
|
|
102
|
+
}
|
|
103
|
+
if (method === "tools/call") {
|
|
104
|
+
const tool = TOOLS.find((t) => t.name === params?.name);
|
|
105
|
+
if (!tool) return err(id, `bilinmeyen araç: ${params?.name}`);
|
|
106
|
+
try {
|
|
107
|
+
const data = await tool.run(params.arguments || {});
|
|
108
|
+
return ok(id, { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] });
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// Hata metni araca DÖNÜYOR, sessizce yutulmuyor: model "veri yok" ile
|
|
111
|
+
// "okuyamadım" arasındaki farkı görmezse yanlış sonuç anlatır.
|
|
112
|
+
return ok(id, { content: [{ type: "text", text: `HATA: ${e.message}` }], isError: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (id !== undefined) err(id, `desteklenmeyen metot: ${method}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let buf = "";
|
|
119
|
+
process.stdin.setEncoding("utf8");
|
|
120
|
+
process.stdin.on("data", async (chunk) => {
|
|
121
|
+
buf += chunk;
|
|
122
|
+
let i;
|
|
123
|
+
while ((i = buf.indexOf("\n")) >= 0) {
|
|
124
|
+
const line = buf.slice(0, i).trim();
|
|
125
|
+
buf = buf.slice(i + 1);
|
|
126
|
+
if (!line) continue;
|
|
127
|
+
try {
|
|
128
|
+
await handle(JSON.parse(line));
|
|
129
|
+
} catch {
|
|
130
|
+
/* bozuk satır yok sayılır — akış kapanmamalı */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "storelift-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "App Store & Google Play keyword ranks, rivals and AI visibility as an MCP server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"storelift-mcp": "index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.mjs",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"modelcontextprotocol",
|
|
16
|
+
"mcp-server",
|
|
17
|
+
"claude",
|
|
18
|
+
"aso",
|
|
19
|
+
"app-store-optimization",
|
|
20
|
+
"app-store",
|
|
21
|
+
"google-play",
|
|
22
|
+
"keyword-rank",
|
|
23
|
+
"ai-visibility"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://storelift.vomcore.com",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"email": "hello@vomcore.com"
|
|
28
|
+
},
|
|
29
|
+
"author": "Vomcore",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|