@open-qr/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.
Files changed (3) hide show
  1. package/README.md +54 -0
  2. package/index.js +63 -0
  3. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @open-qr/mcp
2
+
3
+ MCP server for [OpenQR](https://openqr.uk): generate QR codes and create, edit
4
+ and track dynamic (editable) QR codes with scan analytics.
5
+
6
+ A thin stdio-to-HTTP bridge: it forwards MCP traffic between your client and the
7
+ hosted endpoint at `https://openqr.uk/mcp`. No tools are implemented here, so
8
+ the tool list always matches what the server exposes (currently 17 tools, 4
9
+ prompts, 5 resources).
10
+
11
+ ## Setup
12
+
13
+ 1. Get a free API key at [openqr.uk/api](https://openqr.uk/api).
14
+ 2. Point your MCP client at the command below with the key in the environment.
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "openqr": {
20
+ "command": "npx",
21
+ "args": ["-y", "@open-qr/mcp"],
22
+ "env": { "OPENQR_API_KEY": "oqr_..." }
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ Prefer the hosted endpoint directly? Most remote-capable clients (Claude Web,
29
+ Claude Desktop, Cursor, Cline) can connect to `https://openqr.uk/mcp` over
30
+ streamable HTTP with a `Bearer` Authorization header, no bridge needed.
31
+
32
+ ## Environment
33
+
34
+ | Variable | Required | Purpose |
35
+ | --- | --- | --- |
36
+ | `OPENQR_API_KEY` | yes | Bearer key from [openqr.uk/api](https://openqr.uk/api) |
37
+ | `OPENQR_MCP_URL` | no | Override the endpoint (default `https://openqr.uk/mcp`) |
38
+
39
+ ## Verify
40
+
41
+ With a real key in the environment:
42
+
43
+ ```bash
44
+ npm install
45
+ OPENQR_API_KEY=oqr_... node verify.mjs
46
+ ```
47
+
48
+ That runs the full round-trip a client would: initialize, list tools, list
49
+ prompts and resources, generate a QR, and make an authenticated dynamic-code
50
+ call.
51
+
52
+ ## Licence
53
+
54
+ AGPL-3.0-only, same as the repository.
package/index.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ // Bridge a local stdio MCP client to the hosted OpenQR MCP endpoint
3
+ // (https://openqr.uk/mcp, streamable HTTP, Bearer API key).
4
+ //
5
+ // The bridge is transparent: every JSON-RPC message the client writes to
6
+ // stdio is forwarded to the remote session verbatim, and every message the
7
+ // remote sends back is written to stdio. Nothing is buffered, filtered or
8
+ // re-implemented, so the tool list stays exactly what the server exposes.
9
+ //
10
+ // Usage: OPENQR_API_KEY=oqr_... npx -y @open-qr/mcp
11
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
13
+
14
+ const url = process.env.OPENQR_MCP_URL ?? "https://openqr.uk/mcp";
15
+ const key = process.env.OPENQR_API_KEY;
16
+
17
+ if (!key) {
18
+ console.error(
19
+ "OPENQR_API_KEY is required. Get a free key at https://openqr.uk/api"
20
+ );
21
+ process.exit(1);
22
+ }
23
+
24
+ const remote = new StreamableHTTPClientTransport(new URL(url), {
25
+ requestInit: { headers: { Authorization: `Bearer ${key}` } },
26
+ });
27
+ const stdio = new StdioServerTransport();
28
+
29
+ let warned = false;
30
+ remote.onerror = (error) => {
31
+ // A 401 from the remote surfaces here as a generic stream error; say the
32
+ // useful thing once instead of the SDK's raw message.
33
+ if (!warned && /401|unauthorized|forbidden/i.test(String(error))) {
34
+ warned = true;
35
+ console.error(
36
+ "OpenQR rejected the API key. Check OPENQR_API_KEY (free key: https://openqr.uk/api)."
37
+ );
38
+ }
39
+ console.error(String(error));
40
+ };
41
+ stdio.onerror = (error) => console.error(String(error));
42
+
43
+ remote.onmessage = (message) => stdio.send(message);
44
+ stdio.onmessage = (message) => remote.send(message);
45
+
46
+ let closing = false;
47
+ const close = async () => {
48
+ if (closing) return;
49
+ closing = true;
50
+ for (const transport of [stdio, remote]) {
51
+ try {
52
+ await transport.close();
53
+ } catch {
54
+ // already closed or never opened; nothing to do
55
+ }
56
+ }
57
+ process.exit(0);
58
+ };
59
+ process.on("SIGINT", close);
60
+ process.on("SIGTERM", close);
61
+
62
+ await remote.start();
63
+ await stdio.start();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@open-qr/mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for OpenQR (https://openqr.uk): generate QR codes and create, edit and track dynamic (editable) QR codes with scan analytics. Bridges stdio MCP clients to the hosted endpoint.",
5
+ "license": "AGPL-3.0-only",
6
+ "type": "module",
7
+ "bin": {
8
+ "openqr-mcp": "index.js"
9
+ },
10
+ "files": [
11
+ "index.js",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/open-qr/openqr.git",
20
+ "directory": "mcp"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "qr-code",
26
+ "qr",
27
+ "dynamic-qr",
28
+ "openqr"
29
+ ],
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.30.0"
32
+ }
33
+ }