qrzap-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.
- package/bin.js +2 -0
- package/package.json +27 -0
- package/server.mjs +239 -0
package/bin.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qrzap-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for generating QR codes - URL, WiFi, phone, email, SMS, vCard, text",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"qrzap-mcp": "./bin.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin.js",
|
|
11
|
+
"server.mjs"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"qr",
|
|
16
|
+
"qrcode",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"claude",
|
|
19
|
+
"ai-tools"
|
|
20
|
+
],
|
|
21
|
+
"author": "Pranav Bhatkar",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
25
|
+
"qrcode": "^1.5.4"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/server.mjs
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import QRCode from "qrcode";
|
|
10
|
+
|
|
11
|
+
const QR_TYPES = {
|
|
12
|
+
url: {
|
|
13
|
+
description: "Generate QR code for a URL",
|
|
14
|
+
params: {
|
|
15
|
+
url: { type: "string", description: "The URL to encode" },
|
|
16
|
+
},
|
|
17
|
+
required: ["url"],
|
|
18
|
+
build: ({ url }) => url,
|
|
19
|
+
},
|
|
20
|
+
wifi: {
|
|
21
|
+
description: "Generate QR code for WiFi network credentials",
|
|
22
|
+
params: {
|
|
23
|
+
ssid: { type: "string", description: "Network name (SSID)" },
|
|
24
|
+
password: { type: "string", description: "Network password" },
|
|
25
|
+
encryption: { type: "string", description: "Encryption type: WPA, WEP, or nopass", default: "WPA" },
|
|
26
|
+
hidden: { type: "boolean", description: "Whether the network is hidden", default: false },
|
|
27
|
+
},
|
|
28
|
+
required: ["ssid"],
|
|
29
|
+
build: ({ ssid, password = "", encryption = "WPA", hidden = false }) =>
|
|
30
|
+
`WIFI:T:${encryption};S:${ssid};P:${password};H:${hidden};;`,
|
|
31
|
+
},
|
|
32
|
+
phone: {
|
|
33
|
+
description: "Generate QR code for a phone number",
|
|
34
|
+
params: {
|
|
35
|
+
phone: { type: "string", description: "Phone number with country code" },
|
|
36
|
+
},
|
|
37
|
+
required: ["phone"],
|
|
38
|
+
build: ({ phone }) => `tel:${phone}`,
|
|
39
|
+
},
|
|
40
|
+
email: {
|
|
41
|
+
description: "Generate QR code for an email address with optional subject and body",
|
|
42
|
+
params: {
|
|
43
|
+
email: { type: "string", description: "Email address" },
|
|
44
|
+
subject: { type: "string", description: "Email subject" },
|
|
45
|
+
body: { type: "string", description: "Email body" },
|
|
46
|
+
},
|
|
47
|
+
required: ["email"],
|
|
48
|
+
build: ({ email, subject, body }) => {
|
|
49
|
+
const params = [];
|
|
50
|
+
if (subject) params.push(`subject=${encodeURIComponent(subject)}`);
|
|
51
|
+
if (body) params.push(`body=${encodeURIComponent(body)}`);
|
|
52
|
+
return `mailto:${email}${params.length ? "?" + params.join("&") : ""}`;
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
sms: {
|
|
56
|
+
description: "Generate QR code for an SMS message",
|
|
57
|
+
params: {
|
|
58
|
+
phone: { type: "string", description: "Phone number" },
|
|
59
|
+
message: { type: "string", description: "Pre-filled message text" },
|
|
60
|
+
},
|
|
61
|
+
required: ["phone"],
|
|
62
|
+
build: ({ phone, message }) =>
|
|
63
|
+
`sms:${phone}${message ? "?body=" + encodeURIComponent(message) : ""}`,
|
|
64
|
+
},
|
|
65
|
+
vcard: {
|
|
66
|
+
description: "Generate QR code for a contact card (vCard)",
|
|
67
|
+
params: {
|
|
68
|
+
firstName: { type: "string", description: "First name" },
|
|
69
|
+
lastName: { type: "string", description: "Last name" },
|
|
70
|
+
phone: { type: "string", description: "Phone number" },
|
|
71
|
+
email: { type: "string", description: "Email address" },
|
|
72
|
+
org: { type: "string", description: "Organization name" },
|
|
73
|
+
url: { type: "string", description: "Website URL" },
|
|
74
|
+
},
|
|
75
|
+
required: ["firstName"],
|
|
76
|
+
build: ({ firstName, lastName = "", phone, email, org, url }) => {
|
|
77
|
+
const lines = [
|
|
78
|
+
"BEGIN:VCARD",
|
|
79
|
+
"VERSION:3.0",
|
|
80
|
+
`N:${lastName};${firstName};;;`,
|
|
81
|
+
`FN:${firstName} ${lastName}`.trim(),
|
|
82
|
+
];
|
|
83
|
+
if (phone) lines.push(`TEL:${phone}`);
|
|
84
|
+
if (email) lines.push(`EMAIL:${email}`);
|
|
85
|
+
if (org) lines.push(`ORG:${org}`);
|
|
86
|
+
if (url) lines.push(`URL:${url}`);
|
|
87
|
+
lines.push("END:VCARD");
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
text: {
|
|
92
|
+
description: "Generate QR code for plain text",
|
|
93
|
+
params: {
|
|
94
|
+
text: { type: "string", description: "The text to encode" },
|
|
95
|
+
},
|
|
96
|
+
required: ["text"],
|
|
97
|
+
build: ({ text }) => text,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
function buildToolSchema() {
|
|
102
|
+
const properties = {
|
|
103
|
+
type: {
|
|
104
|
+
type: "string",
|
|
105
|
+
enum: Object.keys(QR_TYPES),
|
|
106
|
+
description: "Type of QR code to generate",
|
|
107
|
+
},
|
|
108
|
+
size: {
|
|
109
|
+
type: "number",
|
|
110
|
+
description: "QR code size in pixels (default: 400)",
|
|
111
|
+
},
|
|
112
|
+
errorCorrection: {
|
|
113
|
+
type: "string",
|
|
114
|
+
enum: ["L", "M", "Q", "H"],
|
|
115
|
+
description: "Error correction level (default: M)",
|
|
116
|
+
},
|
|
117
|
+
format: {
|
|
118
|
+
type: "string",
|
|
119
|
+
enum: ["png", "svg"],
|
|
120
|
+
description: "Output format (default: png)",
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
for (const [, config] of Object.entries(QR_TYPES)) {
|
|
125
|
+
for (const [key, param] of Object.entries(config.params)) {
|
|
126
|
+
if (!properties[key]) {
|
|
127
|
+
properties[key] = { type: param.type, description: param.description };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties,
|
|
135
|
+
required: ["type"],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const server = new Server(
|
|
140
|
+
{ name: "qrzap", version: "1.0.0" },
|
|
141
|
+
{ capabilities: { tools: {} } }
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
145
|
+
tools: [
|
|
146
|
+
{
|
|
147
|
+
name: "generate_qr",
|
|
148
|
+
description:
|
|
149
|
+
"Generate a QR code. Supports: URL, WiFi, phone, email, SMS, vCard, and plain text. Returns base64 PNG or SVG string.",
|
|
150
|
+
inputSchema: buildToolSchema(),
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
156
|
+
if (request.params.name !== "generate_qr") {
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const args = request.params.arguments ?? {};
|
|
164
|
+
const type = args.type;
|
|
165
|
+
const config = QR_TYPES[type];
|
|
166
|
+
|
|
167
|
+
if (!config) {
|
|
168
|
+
return {
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: `Unknown QR type: ${type}. Valid types: ${Object.keys(QR_TYPES).join(", ")}`,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
isError: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const req of config.required) {
|
|
180
|
+
if (!args[req]) {
|
|
181
|
+
return {
|
|
182
|
+
content: [{ type: "text", text: `Missing required field: ${req}` }],
|
|
183
|
+
isError: true,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const data = config.build(args);
|
|
189
|
+
const size = args.size ?? 400;
|
|
190
|
+
const errorCorrection = args.errorCorrection ?? "M";
|
|
191
|
+
const format = args.format ?? "png";
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
if (format === "svg") {
|
|
195
|
+
const svg = await QRCode.toString(data, {
|
|
196
|
+
type: "svg",
|
|
197
|
+
width: size,
|
|
198
|
+
margin: 2,
|
|
199
|
+
errorCorrectionLevel: errorCorrection,
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
content: [{ type: "text", text: svg }],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const dataUrl = await QRCode.toDataURL(data, {
|
|
207
|
+
width: size,
|
|
208
|
+
margin: 2,
|
|
209
|
+
errorCorrectionLevel: errorCorrection,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const base64 = dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
content: [
|
|
216
|
+
{
|
|
217
|
+
type: "image",
|
|
218
|
+
data: base64,
|
|
219
|
+
mimeType: "image/png",
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
} catch (err) {
|
|
224
|
+
return {
|
|
225
|
+
content: [{ type: "text", text: `QR generation failed: ${err.message}` }],
|
|
226
|
+
isError: true,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
async function main() {
|
|
232
|
+
const transport = new StdioServerTransport();
|
|
233
|
+
await server.connect(transport);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
main().catch((err) => {
|
|
237
|
+
console.error("Fatal:", err);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
});
|