APKCube
Android APK intelligence over MCP: search apps, versions and downloads, run security scans, detect SDKs and trackers, inspect permissions and signing history, look up hashes and certificates, and fetch Google Play details. Remote Streamable HTTP with OAuth 2.1 or API key.
Hosted MCP Server
npx add-mcp 'https://apkcube.com/api/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
Connect
-
Add the server to your client
The quickest way is a one-click install, which opens your client with the server filled in. To add it to Claude by hand: Settings → Connectors → Add custom connector. Or add it by hand. Any client that speaks MCP over HTTP and supports OAuth will work; only the file and its shape differ.
Cursor ~/.cursor/mcp.json VS Code .vscode/mcp.json OpenCode ~/.config/opencode/opencode.jsonclaude mcp add --transport http apkcube https://apkcube.com/api/mcp -
Ask in plain language
The agent picks the tools itself. A few things to try:- “Is this APK safe to install? Check the scan, the permissions and the signing key.”
- “What versions of Signal do you have, and which is the newest arm64 build?”
- “Download the latest WhatsApp APK, then tell me its sha256 so I can verify it.”
- “What changed between the last two builds of com.spotify.music? Any new permissions?”
- “Has this package ever changed its signing key?”
- “Find me a torch app with more than 10 million installs and a rating above 4.5.”
Tools
36 tools. Each catalogue tool runs the same code as its REST endpoint, linked under its row, so prices, responses and takedowns are always identical. Every tool declares an outputSchema and returns structured content plus the same JSON as text.
31 of the 36 are marked readOnlyHint, so a client that auto-approves reads can run them without asking. The ones marked catalog:download spend credits on a download, a fetch or a variant build, or change your app's public page, so a client that confirms writes will ask you first.
Find apps
Start here when you have a name or a phrase and need a package id.
| Tool | Credits | What it does |
|---|---|---|
| search_apps | 1 | Search by app name, publisher or package id.GET /apps?q= |
| get_app | 5 | The full listing for one app, including whether it can be downloaded.GET /apps/{pkg} |
| get_apps | 1 / app | Summary rows for up to 100 apps in one call. Charged per app.POST /apps/batch |
| list_developer_apps | 1 | All apps from one publisher, most installed first.GET /developers/{slug} |
| list_similar_apps | 1 | Apps related to one app, as its page on the site shows them.GET /apps/{pkg}/similar |
| list_chart | 1 | Charts: hot, top grossing, latest, new, Editors' Choice, and what's popular or freshly updated here.GET /charts |
| list_categories | free | The category list used to filter searches.GET /categories |
Builds and downloads
List what we hold, get a file, or ask for one we don't have yet.
| Tool | Credits | What it does |
|---|---|---|
| check_availability | free | Free. Whether we have a build of up to 100 apps, each one's newest version, and every version we have by code and name. For update checks.POST /apps/availability |
| list_versions | 5 | All builds we have for an app, newest first, with checksums.GET /apps/{pkg}/versions |
| get_download_url catalog:download | 10 | Get a signed download URL for one build. Expires after about 5 minutes.POST /apps/{pkg}/download |
| request_fetch catalog:download | 10 | Ask us to fetch an app from Google Play when list_versions comes back empty.POST /apps/{pkg}/fetch |
| get_fetch_status | 1 | Whether a fetch you started is still running, ready, or failed.GET /apps/{pkg}/fetch |
| request_variant catalog:download | 10 | Ask for a build the default download doesn't include: every language, 32-bit ARM, universal, an older Android version or a specific device.POST /apps/{pkg}/variants |
| get_variant_status | 1 | Whether a variant build is ready, still building, failed, or not requested yet.GET /apps/{pkg}/variants |
Security and integrity
What is inside a build, who signed it, and what changed between releases.
| Tool | Credits | What it does |
|---|---|---|
| get_version_security | 5 | Checksum, antivirus result, signing certificates, permissions and findings for one build.GET /apps/{pkg}/security |
| list_permissions | 5 | Permissions a build declares, with plain descriptions. Sensitive ones first.GET /apps/{pkg}/permissions |
| list_sdks | 5 | Third-party libraries detected in a build, and how much of the code we could read.GET /apps/{pkg}/sdks |
| list_trackers | 5 | Only the advertising, analytics and tracking libraries in a build.GET /apps/{pkg}/trackers |
| get_protection | 5 | Whether a build is packed or protected, and how that limits the other checks.GET /apps/{pkg}/protection |
| get_manifest | 5 | Manifest settings, native libraries and the findings we flag from them.GET /apps/{pkg}/manifest |
| get_signing_history | 5 | Which key signed each release, and whether the key was ever replaced.GET /apps/{pkg}/signing |
| compare_versions | 1 | Differences between two releases: permissions, signing key, size, min Android.GET /apps/{pkg}/compare |
| get_raw_scan | 10 | The raw scan record, for when you want to apply your own rules.GET /apps/{pkg}/raw |
Reverse lookups
Start from a file you already have rather than an app name.
| Tool | Credits | What it does |
|---|---|---|
| lookup_hash | 5 | Find the build that matches an MD5, SHA-1 or SHA-256 checksum.GET /lookup/hash/{hash} |
| lookup_certificate | 5 | Find all packages we have that were signed by a given certificate.GET /lookup/certificate/{fp} |
| list_developer_certificates | 5 | Every signing key behind one publisher's apps, and whether each also signs anyone else's.GET /developers/{slug}/certificates |
Google Play
The live store listing and its reviews, in any country.
| Tool | Credits | What it does |
|---|---|---|
| get_play_details | 5 | Google Play's full store listing in any country: rating histogram, installs, in-app prices, release date.GET /apps/{pkg}/play |
| list_reviews | 5 | One page of Google Play user reviews, with the developer's replies.GET /apps/{pkg}/reviews |
Account
Free, so an agent can check before it spends.
| Tool | Credits | What it does |
|---|---|---|
| get_account | free | Your remaining credits and which key or connected client is calling.GET /me |
Developers Console
Free, for your own apps. Needs a signed-in user on a verified developer's team: OAuth, not an API key.
| Tool | Credits | What it does |
|---|---|---|
| console_list_apps | free | The apps you manage, with your role on each. |
| console_app_stats | free | Daily downloads of one of your apps over 30 days, the lifetime total and top countries. |
| console_list_uploads | free | Builds you submitted, newest first, with each one's status and any hold or rejection reason. |
| console_upload_status | free | Where one upload stands, with its scan summary once it has one. |
| console_security_report | free | Every stored build of your app with its scan verdict, signing check and any notice on it. |
| console_update_listing catalog:download | free | Change the description, what's new and video on your app's page. Live immediately. |
| console_set_version_notice catalog:download | free | Mark one build vulnerable or withdrawn, optionally blocking its download, or lift the mark. |
Credits
Every tool result reports what the call cost and what's left, in _meta under com.apkcube/credits. That includes tool errors: a charged failure is still a charge, and a refunded one reports a cost of 0.
Running out comes back as a tool error that says what the call costs and where to top up, not as a server failure, so the agent stops instead of retrying.
To check the balance up front, call get_account. It's free, and worth doing before a 10-credit download. It returns the key, the balance and when the account was created, never a name or email. It is the same as GET /me.
get_apps is priced per package (1 credit each) and returns summary rows, not the full listing get_app returns for 5 credits.
A tools/call result
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"resultType": "complete",
"structuredContent": {
"packageName": "org.thoughtcrime.securesms",
"versions": [ … ]
},
"content": [
{ "type": "text", "text": "{\"packageName\":…}" }
],
"isError": false,
"_meta": {
"com.apkcube/credits": {
"cost": 5,
"remaining": 245
}
}
}
}
Without an account (x402)
https://apkcube.com/api/mcp/x402 serves the paid tools with no sign-in at all: each call is paid for by the call itself over x402, in USDC on Base or Solana, at $0.01 per credit. An unpaid call returns a tool error whose structuredContent is the x402 PaymentRequired; an x402-aware client (such as @x402/mcp 's) pays it and repeats the call with the payment in _meta["x402/payment"], and the receipt comes back in _meta["x402/payment-response"].
Payments settle by the credit rules: an empty answer or a failure on our side moves no money. get_account and the free tools are not served there — they need an account — and neither are prompts.
Prompts
8 ready-made prompts, which most clients offer as slash commands. Each walks the agent through a multi-step task and carries the rules that don't fit in one tool description: check the signing key before recommending an install, don't read a missing scan as a clean one, and pick builds by versionCode, not by version name. Prompts cost nothing; the tools they call are charged as usual.
| Prompt | Arguments | What it does |
|---|---|---|
| vet_app | packageName | Decide whether a package is safe to install and explain why, using the listing, scan verdict, permissions and signing key. |
| find_and_download | query requirements? | Find an app by name, pick the right build and mint a download URL, checking first so you don't pay for the wrong file. |
| whats_changed | packageName from?``to? | Compare two builds of a package and report new permissions and any change of signing key. |
| audit_sdks | packageName | List the third-party code inside an app (ads, analytics, attribution, crash reporting) and say how much of the build we could actually read. |
| trace_artifact | artifact | Start from a checksum or a signing-key fingerprint and find out what it is, who signed it, and what else that key signs. |
| privacy_report | packageName | Say what an app can reach and who it reports to: its trackers, its sensitive permissions and the manifest settings that expose data, with how much of the build we could read. |
| compare_apps | a b | Put two apps side by side — rating, installs, trackers and sensitive permissions — so you can choose between alternatives. |
| vet_developer | slug | Check whether a publisher signs its apps consistently, and whether any of its keys also signs somebody else's packages. |
name? marks an optional argument.
Protocol
Streamable HTTP, MCP revision 2026-07-28. Each request carries its own protocol version, so there is no handshake and no session. Clients on an older revision still connect: we answer their initialize without issuing a session. You only need this if you're writing a client; any MCP client does it for you.
The method, tool name and version are mirrored into headers, and the server rejects a request whose headers and body disagree.
A raw tools/call
curl -sS https://apkcube.com/api/mcp \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/call" \
-H "Mcp-Name: list_versions" \
-d '{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "list_versions",
"arguments": {
"packageName": "org.thoughtcrime.securesms"
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28"
}
}
}'
| Transport | Streamable HTTP. POST only, with a body of at most 256 KiB (413 otherwise); GET and DELETE return 405. |
|---|---|
| Version | 2026-07-28, or 2025-11-25 or 2025-06-18 after an initialize. Any other gets a 400 with error -32022 and the list of supported versions. |
| Headers | MCP-Protocol-Version and Mcp-Method on every 2026-07-28 request, plus Mcp-Name on tools/call and prompts/get. A mismatch with the body is a 400 with -32020. |
| Response | A single application/json object. No streaming, no SSE. A notification (no id) gets an empty 202. |
| Sessions | None, in either era. Mcp-Session-Id is never issued and is ignored if sent. |
| Sign-in | Not needed for initialize, server/discover, the two lists or ping, so you can see every tool before connecting. tools/call and prompts/get need a credential. |
| Methods | server/discover, initialize, tools/list, tools/call, prompts/list, prompts/get, ping. Anything else is a 404 with -32601. |
| Caching | server/discover, tools/list and prompts/list are the same for every caller and cacheable for 5 minutes. |
| Origin | A request with an Origin header is accepted only from our own site, against DNS rebinding. Desktop and CLI clients send none. |
Errors follow the spec. An unknown tool or malformed arguments is a JSON-RPC protocol error. Problems the agent can act on, like “package not found”, “app taken down” or “out of credits”, come back as ordinary results with isError: true and a sentence saying what to do.
Authorization
OAuth 2.1, as the MCP spec requires over HTTP. We run both the authorization server and the resource server, so an account here is all you need. Clients discover all of this on their own from the first 401, which comes at the first tool call: listing tools needs no sign-in.
| Protected resource | https://apkcube.com/api/mcp |
|---|---|
| Resource metadata | https://apkcube.com/.well-known/oauth-protected-resource/api/mcp |
| Issuer | https://apkcube.com |
| Server metadata | https://apkcube.com/.well-known/oauth-authorization-server |
| Registration | Dynamic (RFC 7591), open to anyone, rate limited. |
| Flow | Authorization code with PKCE (S256). Refresh tokens issued. |
| Token binding | Tokens are bound to the resource above. A token issued for anything else is rejected. |
Scopes
Reading costs between 0 and 10 credits a call (get_apps is 1 per package). A download costs 10 and is recorded on your account, a fetch 10, and a variant build 10. Those three sit behind their own scope, with the two console tools that change your app's public page, so a client that only looks things up never has to be trusted to spend or publish.
| Scope | Grants |
|---|---|
| catalog:read | Every tool except get_download_url, request_fetch, request_variant, console_update_listing, and console_set_version_notice (31 of 36). Required for every call. |
| catalog:download | get_download_url, request_fetch, request_variant, console_update_listing, and console_set_version_notice: a download, a fetch of an app we don't have yet, a variant the default download doesn't include, or a change to your own app's page on the Developers Console. |
| offline_access | A refresh token, so you don't sign in again every hour. |
A token missing the scope a tool needs gets a 403 with an RFC 6750 insufficient_scope challenge naming the scope, which a client can use to ask you for it.
API keys
For an agent that has no browser to sign in with, the server also accepts the same akc_ key the REST API takes. Send it as a bearer token on every request, in place of the access token in the example above. Most clients have a headers field for this.
{
"Authorization": "Bearer akc_9f3Ka2Lp_xxxxxxxxxxxxxxxxxxxx"
}
A key has no scopes. It can do everything your account can, downloads included, except the Developers Console tools, which need a signed-in user over OAuth. Keep it on a server and never in client-side code. Until your first top-up an account can hold 1 active key; after it, 10. You can revoke a key from your account at any time.
Troubleshooting
| What you see | Why | What to do |
|---|---|---|
| 401 on every tool call | The client didn't sign in with OAuth, or its API key is wrong or revoked. | Use a client with OAuth support, or send an API key as a bearer token. |
| 400, error -32022 | The client asked for a revision we don't speak (older than 2025-06-18, or not yet released). | Update the client. The error's data lists the versions we support. |
| 400, error -32020 | A mirrored header doesn't match the request body. | A bug in a hand-written client: send the headers shown under Protocol. |
| 403 insufficient_scope | The client was authorized without catalog:download and called get_download_url, request_fetch, request_variant, console_update_listing or console_set_version_notice. | Re-authorize from the client, asking for catalog:download. |
| 429 Too many requests | Too many calls from your address in a short time, before any credential is checked. | Wait for the Retry-After header, then retry. |
| 403 Origin not allowed | A browser page on another site called the server directly. | Make the call from that site's backend. |
| 405 | A GET or DELETE, usually a client probing for SSE. | POST only. Use a client that supports this revision. |
| Tool error: Out of credits | The balance is below the call's price. Nothing was charged. | Top up on billing, then retry. |