Developers
The OpenQR MCP server: QR codes your AI agent can create, edit and measure
By OpenQR · updated 26 June 2026
9 min read · Updated 26 June 2026

MCP (the Model Context Protocol) is an open standard for letting an AI agent call external tools. Instead of you writing code against the OpenQR REST API, your agent connects to an MCP server, discovers the tools it exposes, and calls them itself when a request needs them. OpenQR runs a hosted MCP server, so there is nothing to install or self-host: you point your client at one URL, paste a key, and the agent can generate and manage QR codes on your behalf.
REST or MCP: same engine
The MCP server is a thin layer over the same API documented in the API getting-started guide and the dynamic-codes guide. If you are writing code directly, use the REST endpoints. If you are wiring up an agent, use MCP. Both share the same account, keys, rate limits and abuse rules.
The OpenQR MCP endpoint and how auth works
OpenQR exposes a single Streamable HTTP MCP endpoint at https://openqr.uk/mcp, speaking JSON-RPC 2.0 over POST. There is no stdio binary to install and nothing to run locally — it is a hosted server, so any MCP client that supports the HTTP transport can reach it.
Authentication is the same free API key used everywhere else in OpenQR: you send it as Authorization: Bearer oqr_… on the connection. Create one in the dashboard at openqr.uk/api — sign in with a magic link, open API keys, and copy the key (it is shown once and stored only as a SHA-256 hash). Every tool call needs it; an unauthenticated request gets a clear error telling you to add a key rather than silently failing.
The API is key-gated on purpose
The in-browser generator is free and needs no signup. The API and MCP server require a key so OpenQR can keep abuse off the redirect network. Treat the key like a password: it is per-account, scope it to one agent or machine, and rotate it from the dashboard if it leaks.
Connecting from a real client
Most MCP clients — Claude Desktop, Claude Code, Cursor and Cline — take the same shape of config: a server name, the HTTP transport, the URL, and an Authorization header. Add this to the client's MCP settings and restart it:
{
"mcpServers": {
"openqr": {
"type": "http",
"url": "https://openqr.uk/mcp",
"headers": { "Authorization": "Bearer oqr_your_key_here" }
}
}
}- Claude Desktop — add the block to its
mcpServersconfig and restart; the tools appear under the connectors menu. - Claude Code — register the server with
claude mcp addusing the HTTP transport and the same URL and header. - Cursor — add it to your MCP servers settings; the tools become available to the agent in chat.
- Cline — add the same server entry in its MCP configuration.
- OpenClaw — the self-hosted personal AI assistant supports MCP servers too; register
https://openqr.uk/mcpwith the Bearer header via its own MCP config (see its CLI docs for the exact file).
Once connected, the agent calls tools/list automatically and sees everything below. From there you describe the outcome you want in plain English, and the agent picks the tool and fills in the arguments. Prefer code? Import the full OpenAPI 3.1 spec into the same clients for the REST side.
The 18 OpenQR MCP tools
Anything you can do in the dashboard, an agent can do over MCP. The 18 tools fall into seven groups. Argument names below are exact: they are what the server expects.
| Group | Tool | Arguments | What it does |
|---|---|---|---|
| Generate | generate_qr | data (required), format (png|svg), size, theme | Render a static QR image (PNG by default, or SVG markup). A theme applies its colours. |
| Generate | create_static_qr | type (required), fields (required), label, theme | Save a static code from structured fields: wifi, vCard, email, SMS, WhatsApp, geo, text, phone or url. Render the returned payload with generate_qr. |
| Dynamic CRUD | create_dynamic_qr | destination (required), label, theme | Create an editable code; returns its id and oqr.to short link. Pass theme to style it. |
| Dynamic CRUD | update_dynamic_qr | id (required) + any of destination, label, slug, tags, folder_id, theme | Repoint, re-tag or restyle a code without reprinting. Send only what changes. |
| Dynamic CRUD | get_dynamic_qr | id (required) | Full details for one code: destination, short link, label, type, tags, folder, status, created date. |
| Dynamic CRUD | list_dynamic_qr | limit | List your dynamic codes: id, short URL, destination, label, status. |
| Dynamic CRUD | delete_dynamic_qr | id (required) | Permanently delete a code; its short link stops resolving. |
| Analytics | get_scans | id (required) | Scan totals plus top countries for a dynamic code. Window follows the plan: 7 days on Free, 30 on Pro. Devices and referrers are Pro. |
| Bulk | bulk_create_dynamic_qr | codes[] of { destination, label }, theme | Create up to 200 dynamic codes in one call, optionally styled with a theme. |
| Folders | list_folders | n/a | List your folders (id and name). |
| Folders | create_folder | name (required) | Create a folder to organise codes. |
| Folders | delete_folder | id (required) | Delete a folder; its codes are un-filed, not deleted. |
| Themes | list_themes | n/a | List your saved style themes (id and name) so you can apply one by reference. |
| Themes | create_theme | name (required), style (object) | Save a reusable style (colours, dot/corner shapes, logo, gradient, frame). |
| Themes | delete_theme | id (required) | Delete a saved theme; codes already styled with it keep their look. |
| Subdomain | get_subdomain | n/a | Report the branded subdomain ({name}.oqr.to) and whether it is suspended. |
| Subdomain | set_subdomain | subdomain (required) | Claim or change the branded subdomain so codes also resolve at {name}.oqr.to. Pro only; printed codes keep working on the plain short link either way. |
| Subdomain | clear_subdomain | n/a | Release the branded subdomain. Codes stay live on the plain short link; the name becomes claimable by someone else. |
Alongside the tools, prompts/list returns four ready-made OpenQR workflows: create_tracked_qr, bulk_qr_for_print, brand_a_qr and qr_scan_report. A prompt is a client-surfaced template, not a tool call: your client shows it as a one-click starting point, and picking it walks the agent through the underlying tool calls. resources/list exposes OpenQR's built-in style presets (Classic, Teal, High Contrast, Dots, Classy) as readable JSON an agent can pass to create_theme.
Three things worth knowing
get_scans follows the owner's plan: 7 days of history on Free, 30 on Pro, and devices/referrers appear on Pro only. update_dynamic_qr is a partial update: pass folder_id to move a code into a folder and folder_id: null to take it out. The theme argument on generate/create/update/bulk accepts a theme's id or its name.
Example prompts that drive the tools
You never name the tools yourself. You describe the outcome and the agent maps it. A few that work well:
- “Make a QR code for https://openqr.uk and save it as a 1024px PNG.” →
generate_qr - “Create a dynamic code pointing at our spring-sale page, label it Spring Sale.” →
create_dynamic_qr - “That campaign moved — repoint the Spring Sale code to the new URL.” →
update_dynamic_qr - “How many scans has the Spring Sale code had, and where from?” →
get_scans - “Make editable codes for each of these 50 product URLs.” →
bulk_create_dynamic_qr - “Put all the restaurant table codes in a folder called Tables.” →
create_folder+update_dynamic_qr - “Save our brand style as a theme called Brand, then make all the new codes use it.” →
create_theme+create_dynamic_qrwiththeme
Worked example 1: a support/marketing agent
A marketing agent is asked to launch a campaign and report back on it later. First it creates the dynamic code — when you ask it to, it issues this tool call (shown here as the raw JSON-RPC a client would send, so you can see exactly what crosses the wire):
curl -X POST https://openqr.uk/mcp \
-H "Authorization: Bearer oqr_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_dynamic_qr",
"arguments": {
"destination": "https://example.com/spring-sale",
"label": "Spring Sale"
}
}
}'The server replies with a text result the agent can read back to you — the new code's id and its oqr.to/<slug> short link. You print the code on the poster. A week later you ask “how is Spring Sale doing?” and the agent calls get_scans with that id:
curl -X POST https://openqr.uk/mcp \
-H "Authorization: Bearer oqr_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_scans",
"arguments": { "id": "the-code-id-from-step-1" }
}
}'Back comes a plain-text summary: total scans, scans over the account's window (7 days on Free, 30 on Pro), and the top countries, plus devices and referrers on Pro. Because the destination is dynamic, the same agent can repoint it the moment the sale ends: call update_dynamic_qr with the id and a new destination, and every already-printed code now lands somewhere else. That edit-without-reprint loop is the whole point of dynamic codes over the API.
Worked example 2: bulk-creating from a list
Suppose an agent has a list of product URLs and needs an editable code for each. Rather than calling create_dynamic_qr in a loop, it sends one bulk_create_dynamic_qr call with up to 200 codes. In JavaScript, the same call against the MCP endpoint looks like this:
const products = [
{ destination: "https://shop.example.com/p/1001", label: "Mug" },
{ destination: "https://shop.example.com/p/1002", label: "T-shirt" },
{ destination: "https://shop.example.com/p/1003", label: "Sticker pack" },
];
const res = await fetch("https://openqr.uk/mcp", {
method: "POST",
headers: {
Authorization: "Bearer oqr_your_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 3,
method: "tools/call",
params: {
name: "bulk_create_dynamic_qr",
arguments: { codes: products },
},
}),
});
const { result } = await res.json();
console.log(result.content[0].text); // ids + oqr.to links for each created codeThe result lists every created code with its id and short link. A natural next step for the agent: create_folder named “Products”, then update_dynamic_qr on each id with that folder_id to file them. For the deeper patterns — chunking large lists, handling rate limits, organising at scale — see bulk generation and folders.
The limits still apply
MCP shares the REST account's rules: dynamic-code creation is rate-limited (300 per hour per account), bulk is capped at 200 per call, and destinations must be public http(s) URLs. Private hosts and oqr.to self-loops are rejected. If a call fails, the tool returns the error text so the agent can explain it rather than guessing.
Where this fits
MCP is the agent layer. If you are building an LLM feature or automation that needs QR codes as one capability among many, you will want both this and the raw API — start with the API getting-started guide for the fundamentals, then read QR codes in LLM apps and workflows for how to weave generation, dynamic links and analytics into a product. OpenQR is free, open-source and watermark-free; the MCP server and the API are free with an account, the free tier includes 1 dynamic code and 7-day analytics, and Pro (£9/mo) lifts that to 25 active codes and a 90-day history window.
Get your free API keySign in with a magic link, create a key, and connect your agent to https://openqr.uk/mcp.