About Appqr
Appqr is a CLI and Node.js package for creating QR codes that route iOS and Android users to their respective app stores. Developers can create campaign-specific codes without changing the hosted redirect file; an MCP server also lets compatible agents run setup and campaign tasks.
Key features
- 01Device-based App Store and Google Play redirects
- 02Generate QR images for campaigns
- 03Separate iOS and Android tracking parameters
- 04Validate store URLs
- 05CLI, Node.js package, and MCP tools
Use cases
- 01Create one QR code for an app listed on both stores
- 02Produce distinct QR images for marketing campaigns
- 03Generate QR files through an MCP-compatible agent
MCP support
Its built-in MCP server exposes setup, campaign QR generation, and store URL validation tools to compatible AI agents.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
Tools
Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.
Details
From the project READMEYou made an app. It's on both stores. You need one QR code for your landing page, business card, and press kit. Every other tool charges a monthly subscription for this. AppQR is free, runs on your machine, and works forever.
npx appqr setup \
--ios "https://apps.apple.com/app/id123456789" \
--android "https://play.google.com/store/apps/details?id=com.myapp" \
--url "https://myapp.com/go"
✓ redirect.html → ./appqr-output/redirect.html
✓ qr.png → ./appqr-output/qr.png
→ Upload redirect.html to your site at: https://myapp.com/go
→ Done! For future campaigns, run: appqr campaign
How it works
Scan QR
↓
Phone opens: myapp.com/go?d=eyJpb3MiOiJodHRw...
↓
redirect.html detects device (client-side JS, no server)
↓
iOS user → App Store
Android → Google Play
The QR code encodes both store URLs as base64 in the URL itself. redirect.html is a tiny generic file that decodes and redirects — it contains zero campaign data and never needs to change. Every new campaign just generates a new QR image.
No AppQR servers are ever in the redirect chain. Everything runs on the developer's own website.
Install
npm install -g appqr
Or run without installing:
npx appqr setup ...
Usage
Step 1 — Setup (once per app)
appqr setup \
--ios "https://apps.apple.com/app/id123456789" \
--android "https://play.google.com/store/apps/details?id=com.myapp" \
--url "https://myapp.com/go"
With campaign tracking on your first QR:
appqr setup \
--ios "https://apps.apple.com/app/id123456789" \
--android "https://play.google.com/store/apps/details?id=com.myapp" \
--url "https://myapp.com/go" \
--ios-params "ct=launch&pt=12345&mt=8" \
--android-params "utm_source=launch&utm_campaign=v1&utm_medium=organic" \
--output ./public/go
Upload redirect.html to your site. That's the only file you ever upload. Never touch it again.
Step 2 — New campaign QR (as many times as you want)
appqr campaign \
--ios "https://apps.apple.com/app/id123456789" \
--android "https://play.google.com/store/apps/details?id=com.myapp" \
--url "https://myapp.com/go" \
--ios-params "ct=instagram_launch&pt=12345&mt=8" \
--android-params "utm_source=instagram&utm_campaign=launch&utm_medium=paid" \
--output ./qrs/instagram
✓ qr.png → ./qrs/instagram/qr.png
iOS URL: https://apps.apple.com/app/id123456789?ct=instagram_launch&pt=12345&mt=8
Android URL: https://play.google.com/store/apps/details?id=com.myapp&utm_source=instagram...
No file upload needed — redirect.html already handles this.
No upload. No website change. Just a new QR image for each campaign.
Show campaign param formats
appqr params
Use as a Node.js package
import { setup, campaign } from 'appqr'
// First time setup
const result = await setup({
ios: 'https://apps.apple.com/app/id123456789',
android: 'https://play.google.com/store/apps/details?id=com.myapp',
hostedUrl: 'https://myapp.com/go',
iosParams: 'ct=launch&pt=12345&mt=8',
androidParams: 'utm_source=launch&utm_campaign=v1&utm_medium=organic',
outputPath: './public/go'
})
// → result.redirectPath upload this once
// → result.qrPath use this as your QR image
// Campaign QR — no upload needed
const qr = await campaign({
ios: 'https://apps.apple.com/app/id123456789',
android: 'https://play.google.com/store/apps/details?id=com.myapp',
hostedUrl: 'https://myapp.com/go',
iosParams: 'ct=instagram_launch&pt=12345&mt=8',
androidParams: 'utm_source=instagram&utm_campaign=launch&utm_medium=paid',
outputPath: './qrs/instagram'
})
// → qr.qrPath your campaign QR image
Full TypeScript types included. No @types/appqr needed.
MCP — For AI agents (Claude, Cursor, Copilot)
AppQR ships with a built-in MCP (Model Context Protocol) server. AI agents that support MCP can generate smart QR codes natively — no CLI knowledge, no manual steps.
Add to Claude Desktop
{
"mcpServers": {
"appqr": {
"command": "npx",
"args": ["appqr", "mcp"]
}
}
}
Add to Cursor
{
"mcpServers": {
"appqr": {
"command": "npx",
"args": ["appqr", "mcp"]
}
}
}
After connecting, the agent can:
"Generate a smart QR code for my app. App Store: apps.apple.com/app/id123. Play Store: play.google.com/store/apps/details?id=com.app. Host it at myapp.com/go."
The agent calls appqr_setup or appqr_campaign directly and gets back the file paths.
MCP tools exposed
| Tool | Description |
|---|---|
appqr_setup | First time setup — generates redirect.html + qr.png |
appqr_campaign | New campaign QR — generates qr.png only |
appqr_validate | Validate store URLs before generating |
Campaign parameter formats
Apple App Store
| Param | What it is | Required | Example |
|---|---|---|---|
ct | Campaign token — your label | Recommended | ct=instagram_launch |
pt | Provider token — your numeric Apple ID | Recommended | pt=12345 |
mt | Media type — always 8 for apps | Yes | mt=8 |
--ios-params "ct=instagram_launch&pt=12345&mt=8"
Google Play (standard UTM)
| Param | What it is | Required | Example |
|---|---|---|---|
utm_source | Traffic source | Yes | utm_source=instagram |
utm_medium | Marketing medium | Yes | utm_medium=paid |
utm_campaign | Campaign name | Yes | utm_campaign=launch |
utm_content | Specific ad or creative | No | utm_content=banner_v1 |
utm_term | Keyword for search ads | No | utm_term=todo+app |
--android-params "utm_source=instagram&utm_campaign=launch&utm_medium=paid"
Why not just use Bitly / QR Tiger / Branch?
| Others | AppQR | |
|---|---|---|
| Smart iOS/Android redirect | ✓ paid | ✓ free |
| Pricing | $15–40/month | free |
| Your QR works if they shut down | ✗ | ✓ always |
| Campaign tracking params | ✓ paid | ✓ free |
| Separate Apple & Google params | ✗ rarely | ✓ yes |
| Agent / CLI friendly | ✗ | ✓ yes |
| MCP server | ✗ | ✓ yes |
| Your campaign data on their server | ✓ always | ✗ never |
Repository structure
appqr/
├── public/
│ ├── index.html # Landing page
│ └── llms.txt # AI agent discovery
├── api/
│ └── ping.js # Telemetry (Vercel serverless)
├── cli/
│ ├── bin/appqr.js # CLI entry point
│ ├── src/
│ │ ├── core.js # Core logic
│ │ ├── mcp.mjs # MCP server
│ │ ├── telemetry.js # Anonymous counters
│ │ ├── index.js # npm package exports
│ │ └── index.d.ts # TypeScript types
│ └── package.json
├── vercel.json
└── README.md # This file
Deploy the web backend (Vercel + Supabase)
The web backend hosts the landing page and collects anonymous usage telemetry (install count, usage count, agent vs human split — no personal data).
1. Deploy to Vercel
git clone https://github.com/harishakumarn/appqr
cd appqr
vercel deploy
Or connect via the Vercel dashboard — import the repo, one click deploy.
2. Set up Supabase (optional — for telemetry)
Run this SQL in your Supabase project:
create table counters (
id integer primary key default 1,
installs bigint default 0,
setups bigint default 0,
human_runs bigint default 0,
agent_runs bigint default 0,
qrs_generated bigint default 0,
constraint single_row check (id = 1)
);
insert into counters (id) values (1);
create or replace function increment_counter(col_name text)
returns void as $$
begin
execute format(
'update counters set %I = %I + 1 where id = 1',
col_name, col_name
);
end;
$$ language plpgsql;
Add to Vercel environment variables:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your-service-role-key
3. Update telemetry URL
In cli/src/telemetry.js, replace the TELEMETRY_URL with your Vercel deployment URL.
4. Publish to npm
cd cli
npm publish
Privacy & telemetry
AppQR sends one anonymous ping per run. No URLs, no campaign data, no store links, no personal information, no device identifiers are ever collected.
The only thing tracked is a counter increment for one of:
installed, setup, human_run, agent_run, qr_generated
Opt out permanently:
echo 'export APPQR_NO_TELEMETRY=1' >> ~/.zshrc
Or per-run:
APPQR_NO_TELEMETRY=1 appqr setup ...
Contributing
PRs welcome. The codebase is intentionally small — core logic is under 200 lines.
git clone https://github.com/harishakumarn/appqr
cd appqr/cli
npm install
node bin/appqr.js setup --help
License
MIT © AppQR Contributors
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.