Kranked MCP
An MCP server that exposes App Store keyword intelligence — keyword difficulty, popularity, and live rank — as tools any MCP client (Claude, etc.) can call. It's the headless companion to the Kranked ASO app: same scoring, no GUI.
Stateless and zero-config: every tool is a live call to Apple's public endpoints. Ranks
come from the App Store's own search ordering; the iTunes Search API is used for app
discovery and metadata, and search-hints for keyword popularity. No database, no API
keys. Runs locally via uvx or as a hosted server.
Speaks MCP 2026-07-28 (Python SDK v2), and stays backwards compatible with 2025-era
clients from the same server. Era is selected per request from the MCP-Protocol-Version
header, so over stdio — which has no headers — the server answers the 2025 initialize
handshake. Nothing to configure either way; clients negotiate it themselves.
Tools (free / open core)
| Tool | What it answers |
|---|---|
search_apps |
Find apps (and their app_id) matching a term |
check_keyword |
One-shot report: difficulty + popularity + KEI + competitors, and your app's rank |
keyword_difficulty |
How hard a keyword is to rank for (0–100), with the top-10 competitors |
keyword_popularity |
How searched a keyword is (0–100) |
popular_keywords |
The most-searched keywords in a category, ranked |
keyword_suggestions |
Apple's autocomplete hints for a seed term |
All tools take a two-letter country (default us).
How the scores work
- Difficulty (0–100) — analyzes the top-10 ranking apps' review volume and rating quality. More established competitors = harder. Labeled Very Easy → Very Hard.
- Popularity (0–100) — two sources, and every response says which one it used.
With Apple Ads credentials configured it is Apple's own
searchPopularity, a real search-volume index. Without them,keyword_popularityfalls back to a free suggest-based proxy (80 = Apple auto-suggests the exact term, 50 = a related term, 20 = neither) andpopular_keywordsreportssource: unconfiguredrather than inventing numbers. The two scales are not comparable — checksourcebefore ranking terms against each other. - KEI — Keyword Efficiency Index = popularity / difficulty. Higher is a better bet.
- Rank comes from the App Store's own search ordering, not the iTunes Search API. The documented Search API does not return the store's ordering: measured across five US terms it matched on 29/60 positions against 60/60 for the store endpoint, and it caps out around 150–185 results instead of 200. The Search API is still used for app discovery and metadata.
Real popularity numbers (BYOK)
searchPopularity comes from Apple's own Ads Platform API, so it needs an Apple Search Ads
account — your account, keys stay on your machine, no ad spend required. Install the
extra and set the credentials:
uvx --with 'kranked-mcp[appleads]' kranked-mcp
| Env var | Purpose |
|---|---|
KRANKED_APPLEADS_CLIENT_ID |
Ads API client id |
KRANKED_APPLEADS_TEAM_ID |
Ads API team id |
KRANKED_APPLEADS_KEY_ID |
Ads API key id |
KRANKED_APPLEADS_ORG_ID |
your Ads account (org) id |
KRANKED_APPLEADS_PRIVATE_KEY |
the ES256 signing key as PEM text… |
KRANKED_APPLEADS_PRIVATE_KEY_PATH |
…or a path to the PEM file instead |
All five are required (either key form counts as one).
Unconfigured is a normal state: the tools degrade to the free proxy instead of failing.
Install
On PyPI — nothing to clone or build. uvx fetches and runs it on demand. Needs Python
3.10+ and uv (brew install uv). Add to your MCP client
config:
{
"mcpServers": {
"kranked": {
"command": "uvx",
"args": ["kranked-mcp"]
}
}
}
For Claude Code: claude mcp add kranked -- uvx kranked-mcp
Source
The sdist on PyPI carries the full source and the test suite — it's on the
Download files tab. Unpack it and
uv sync, then point your client at that directory:
{
"mcpServers": {
"kranked": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/kranked-mcp", "run", "kranked-mcp"]
}
}
}
Run the tests — they're offline (pure scoring plus server wiring driven through the real ASGI app), so they don't hit Apple and can't flake on rate limiting:
uv run pytest
Self-hosting (remote MCP)
Kranked runs in two modes from the same code:
| Mode | Transport | Use | Command |
|---|---|---|---|
| Local | stdio | one user, on your machine | kranked-mcp |
| Hosted | streamable-HTTP at /mcp |
shared server, many users | kranked-mcp-serve |
The hosted server is stateless, so it scales horizontally — no sticky routing, no session
affinity. Since MCP 2026-07-28 that is the protocol's own model rather than an opt-in: there
is no initialize handshake and no session id, and clients negotiate up front with
server/discover instead. Deploy the included Dockerfile to any container host (Railway,
Fly, Render, …); it reads PORT from the environment and exposes GET /health for liveness
checks.
server/discover and tools/list carry a one-hour public cache hint (ttlMs /
cacheScope), so clients can hold the tool list instead of re-listing it every session.
Deploy to Railway
Deployment is declared in .railway/railway.ts (Railway Infrastructure as Code): GitHub
source, Dockerfile build, /health healthcheck, restart policy. To deploy:
# one-time
npm i -g @railway/cli && railway login
# from the repo root
railway init # create/select a project
railway up # build the Dockerfile and deploy
railway domain # get a public https URL
The upstream repo is connected as the service's GitHub source, so pushing to main deploys.
Set KRANKED_CACHE_TTL in the service's Variables if you want a longer/shorter cache.
Verify against the live server, not the build log.
check_keywordfor "keyword tracker" should reporttotal_resultsaround 247. A number in the low 180s means the old iTunes Search API path is answering and the deploy did not take. This is not theoretical: before auto-deploy was connected, the server sat on 2026-08-01 code for a month, and served old ranks for a full day after the fix was merged, because pushing looked like deploying.
Changing deployment config
.railway/railway.ts replaced the deprecated railway.toml (which stops working
2026-12-01). To edit it:
npm install railway --no-save # needed only for plan/apply; not a project dependency
railway config plan # preview
railway config apply --yes # push
Two things worth knowing if you regenerate it. railway config migrate is lossy — it
comments out builder and dockerfilePath, which would have silently left this service on
Railpack rather than the Dockerfile. And railway config pull returns only what lives in
Railway's own settings, so anything that used to come from railway.toml has to be carried
across by hand. restartPolicyType is deliberately absent: ON_FAILURE is Railway's
default, so it round-trips as null and leaves the plan permanently dirty.
Then point a client at the URL:
claude mcp add --transport http kranked https://your-app.up.railway.app/mcp
Config
| Env var | Default | Purpose |
|---|---|---|
PORT / HOST |
8000 / 0.0.0.0 |
bind address |
KRANKED_CACHE_TTL |
3600 |
seconds to cache upstream responses (0 disables) |
KRANKED_CACHE_MAX |
2000 |
max cached entries |
Caching is not optional for a hosted deployment. All users' requests leave from one IP, so without it you hit Apple's per-IP throttle immediately. Difficulty/popularity change slowly, so cached results stay useful for hours. For multiple instances, move the cache to Redis.
Setting KRANKED_API_KEY gates every endpoint except GET /health behind a bearer token;
unauthenticated callers get a 401. Leave it unset and the server is open, which is what you
want for a local stdio run. Usage metering for a paid tier is still roadmap.
Rate limiting
The iTunes API throttles unauthenticated callers per IP with a 403/429 and a non-JSON body. The server serializes requests through a global throttle and retries throttles with exponential backoff + jitter, so it degrades gracefully instead of surfacing bogus decoding errors.
License
MIT © Akos Komuves
Metadata
Release files for kranked-mcp 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| kranked_mcp-0.2.3.tar.gz | 31.7 kB | With provenance | Details for kranked_mcp-0.2.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| kranked_mcp-0.2.3-py3-none-any.whl | Python 3 | none | any | Details for kranked_mcp-0.2.3-py3-none-any.whl |
Total release size: 62.3 kB
Release files / kranked_mcp-0.2.3.tar.gz
| Download URL | kranked_mcp-0.2.3.tar.gz |
|---|---|
| Size | 31.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07fda7b0325ae76c6c8594e3f056492ece0d7d934c709a4e00e27d3ffef44bcd
|
|
BLAKE2b-256 checksum How to use checksums |
dffa7a48a3ac0f5091922234c824a1b01c4e43553695ecbdc0b348874a1483bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / kranked_mcp-0.2.3-py3-none-any.whl
| Download URL | kranked_mcp-0.2.3-py3-none-any.whl |
|---|---|
| Size | 30.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd0885520bd34e05c6e12b811be38b860438b0a3f175cc84b2e755a106898348
|
|
BLAKE2b-256 checksum How to use checksums |
20248dc62e45147d1aaf99ef51d536dcefc9d6b059932da6bf509e02af8fcd22
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logSecurity for kranked-mcp 0.2.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for kranked_mcp-0.2.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.