Skip to main content
Glama

MCP QR Code Server

by jwalsh
index.ts22.7 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { z } from 'zod' import { generateQRCode } from './qrcode.js' // Create an MCP server const server = new McpServer({ name: 'QR Code Generator', version: '0.3.6', }) // Register capabilities server.server.registerCapabilities({ resources: { root: 'qrcode://', get: true, list: true, }, prompts: { list: true, }, }) // Add the QR code generation tool server.tool( 'generate-qrcode', 'Generate QR codes in various formats with customizable error correction levels and sizes', { content: z.string().describe('The text content to encode in the QR code'), errorCorrectionLevel: z .enum(['L', 'M', 'Q', 'H']) .describe('Error correction level (L: 7%, M: 15%, Q: 25%, H: 30%)') .optional() .default('M'), size: z.number().min(1).max(10).describe('Size of the QR code (1-10)').optional().default(3), format: z .enum(['image', 'text']) .describe("Output format: 'image' for PNG QR code, 'text' for terminal-friendly output") .optional() .default('image'), }, async (args, _extra) => { try { const { content, errorCorrectionLevel, size, format } = args // Convert size from 1-10 scale to pixel size const pixelSize = size * 100 if (format === 'text') { // Generate QR code in UTF8/terminal format const result = await generateQRCode({ content, size: pixelSize, errorCorrectionLevel, format: 'terminal', }) return { content: [ { type: 'text', text: `QR code generated for: ${content}`, }, { type: 'text', text: result.data, }, ], } } else { // Generate QR code as PNG and return as base64 const result = await generateQRCode({ content, size: pixelSize, errorCorrectionLevel, format: 'base64', }) // Ensure we're sending clean base64 data // Remove any data URL prefixes if present const imageData = result.data.startsWith('data:') ? result.data.split(',')[1] : result.data return { content: [ { type: 'text', text: `QR code generated for: ${content}`, }, { type: 'image', data: imageData, mimeType: 'image/png', }, ], } } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) return { content: [{ type: 'text', text: `Failed to generate QR code: ${errorMessage}` }], isError: true, } } } ) // Set up resource handlers manually to work with the MCP protocol const resourcesListRequestSchema = z.object({ method: z.literal('resources/list'), params: z.object({}), }) server.server.setRequestHandler(resourcesListRequestSchema, async () => { // Return a list of available QR code resources return { resources: [ { uri: 'qrcode://sample', name: 'Sample QR Code', description: 'A sample QR code that links to the project repository', }, { uri: 'qrcode://hello-world', name: 'Hello World', description: 'A simple Hello World QR code example', }, { uri: 'qrcode://wifi?ssid=MyNetwork&password=password123&encryption=WPA', name: 'WiFi QR Code', description: 'Generate a QR code to connect to WiFi', }, { uri: 'qrcode://contact?name=John%20Doe&phone=555-123-4567&email=john@example.com', name: 'Contact QR Code', description: 'Generate a QR code with contact information', }, { uri: 'qrcode://url?url=https://anthropic.com', name: 'URL QR Code', description: 'Generate a QR code for a URL', }, { uri: 'qrcode://text?content=Hello%20World!&size=250&level=Q', name: 'Text QR Code', description: 'Generate a QR code containing plain text', }, { uri: 'qrcode://calendar?title=Team%20Meeting&start=2025-03-15T10:00:00&end=2025-03-15T11:00:00', name: 'Calendar Event QR Code', description: 'Generate a QR code for a calendar event', }, ], } }) // Add resource retrieval method const resourcesGetRequestSchema = z.object({ method: z.literal('resources/get'), params: z.object({ uri: z.string().describe('The resource URI to retrieve'), }), }) server.server.setRequestHandler(resourcesGetRequestSchema, async request => { const uri = request.params.uri try { // Validate URI format if (!uri.startsWith('qrcode://')) { throw new Error('Resource URI must start with qrcode://') } // Determine resource type and parameters let content = '' let size = 300 let errorCorrectionLevel: 'L' | 'M' | 'Q' | 'H' = 'M' if (uri === 'qrcode://sample') { // Sample QR code const result = await generateQRCode({ content: 'https://github.com/jwalsh/mcp-server-qrcode', size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: 'Sample QR Code for MCP QR Code Server', uri: 'qrcode://sample', }, { uri: 'qrcode://sample', blob: result.data, mimeType: 'image/png', }, ], } } else if (uri === 'qrcode://hello-world') { // Hello World QR code const result = await generateQRCode({ content: 'Hello, World!', size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: 'Hello World QR Code Example', uri: 'qrcode://hello-world', }, { uri: 'qrcode://hello-world', blob: result.data, mimeType: 'image/png', }, ], } } else if (uri.startsWith('qrcode://wifi?')) { // WiFi template const queryString = uri.split('?')[1] || '' const queryParams = new URLSearchParams(queryString) const ssid = queryParams.get('ssid') if (!ssid) { throw new Error('SSID is required for WiFi QR code') } const password = queryParams.get('password') || '' const encryption = queryParams.get('encryption')?.toUpperCase() || 'WPA' // Generate WiFi connection string in standard format // Format: WIFI:T:<encryption>;S:<ssid>;P:<password>;; const wifiContent = `WIFI:T:${encryption};S:${ssid};P:${password};;` const result = await generateQRCode({ content: wifiContent, size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: `WiFi QR Code for "${ssid}"`, uri: uri, }, { uri: uri, blob: result.data, mimeType: 'image/png', }, ], } } else if (uri.startsWith('qrcode://contact?')) { // Contact template const queryString = uri.split('?')[1] || '' const queryParams = new URLSearchParams(queryString) const name = queryParams.get('name') if (!name) { throw new Error('Name is required for contact QR code') } const phone = queryParams.get('phone') || '' const email = queryParams.get('email') || '' const organization = queryParams.get('organization') || '' const title = queryParams.get('title') || '' const url = queryParams.get('url') || '' const address = queryParams.get('address') || '' // Generate contact string in vCard format const vcard = [ 'BEGIN:VCARD', 'VERSION:3.0', `FN:${name}`, phone ? `TEL:${phone}` : '', email ? `EMAIL:${email}` : '', organization ? `ORG:${organization}` : '', title ? `TITLE:${title}` : '', url ? `URL:${url}` : '', address ? `ADR:;;${address};;;` : '', 'END:VCARD', ] .filter(line => line) .join('\n') const result = await generateQRCode({ content: vcard, size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: `Contact QR Code for "${name}"`, uri: uri, }, { uri: uri, blob: result.data, mimeType: 'image/png', }, ], } } else if (uri.startsWith('qrcode://url?')) { // URL template const queryString = uri.split('?')[1] || '' const queryParams = new URLSearchParams(queryString) const url = queryParams.get('url') if (!url) { throw new Error('URL is required for URL QR code') } // Parse size parameter if present if (queryParams.has('size')) { const sizeParam = parseInt(queryParams.get('size')!, 10) if (!isNaN(sizeParam)) { size = sizeParam } } // Parse error correction level if present const levelParam = queryParams.get('level') if (levelParam && ['L', 'M', 'Q', 'H'].includes(levelParam)) { errorCorrectionLevel = levelParam as 'L' | 'M' | 'Q' | 'H' } const result = await generateQRCode({ content: url, size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: `URL QR Code for "${url}"`, uri: uri, }, { uri: uri, blob: result.data, mimeType: 'image/png', }, ], } } else if (uri.startsWith('qrcode://text?')) { // Text template const queryString = uri.split('?')[1] || '' const queryParams = new URLSearchParams(queryString) const content = queryParams.get('content') if (!content) { throw new Error('Content is required for text QR code') } // Parse size parameter if present if (queryParams.has('size')) { const sizeParam = parseInt(queryParams.get('size')!, 10) if (!isNaN(sizeParam)) { size = sizeParam } } // Parse error correction level if present const levelParam = queryParams.get('level') if (levelParam && ['L', 'M', 'Q', 'H'].includes(levelParam)) { errorCorrectionLevel = levelParam as 'L' | 'M' | 'Q' | 'H' } const result = await generateQRCode({ content: decodeURIComponent(content), size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: `Text QR Code: "${decodeURIComponent(content)}"`, uri: uri, }, { uri: uri, blob: result.data, mimeType: 'image/png', }, ], } } else if (uri.startsWith('qrcode://calendar?')) { // Calendar template const queryString = uri.split('?')[1] || '' const queryParams = new URLSearchParams(queryString) const title = queryParams.get('title') if (!title) { throw new Error('Title is required for calendar event QR code') } const start = queryParams.get('start') if (!start) { throw new Error('Start time is required for calendar event QR code') } const end = queryParams.get('end') || '' const location = queryParams.get('location') || '' const description = queryParams.get('description') || '' // Generate calendar string in iCalendar format const uid = `event-${Date.now()}@qrcode-generator` const now = new Date().toISOString().replace(/[-:.]/g, '').slice(0, 15) + 'Z' const ical = [ 'BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//MCP QR Code Generator//EN', 'BEGIN:VEVENT', `UID:${uid}`, `DTSTAMP:${now}`, `DTSTART:${start.replace(/[-:.]/g, '').slice(0, 15)}Z`, end ? `DTEND:${end.replace(/[-:.]/g, '').slice(0, 15)}Z` : '', `SUMMARY:${title}`, location ? `LOCATION:${location}` : '', description ? `DESCRIPTION:${description}` : '', 'END:VEVENT', 'END:VCALENDAR', ] .filter(line => line) .join('\r\n') // Parse size and error correction parameters if (queryParams.has('size')) { const sizeParam = parseInt(queryParams.get('size')!, 10) if (!isNaN(sizeParam)) { size = sizeParam } } const levelParam = queryParams.get('level') if (levelParam && ['L', 'M', 'Q', 'H'].includes(levelParam)) { errorCorrectionLevel = levelParam as 'L' | 'M' | 'Q' | 'H' } const result = await generateQRCode({ content: ical, size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: `Calendar Event QR Code: "${title}"`, uri: uri, }, { uri: uri, blob: result.data, mimeType: 'image/png', }, ], } } else { // Custom resource - Extract content and parameters from the URI const contentMatch = uri.match(/^qrcode:\/\/(.+?)(?:\?|$)/) content = contentMatch ? contentMatch[1] : '' if (!content) { throw new Error('Content parameter is required') } // Extract query parameters const queryString = uri.includes('?') ? uri.split('?')[1] : '' const queryParams = new URLSearchParams(queryString) // Parse size parameter if present if (queryParams.has('size')) { const sizeParam = parseInt(queryParams.get('size')!, 10) if (!isNaN(sizeParam)) { size = sizeParam } } // Parse error correction level if present const levelParam = queryParams.get('level') if (levelParam && ['L', 'M', 'Q', 'H'].includes(levelParam)) { errorCorrectionLevel = levelParam as 'L' | 'M' | 'Q' | 'H' } // Generate QR code const result = await generateQRCode({ content: decodeURIComponent(content), size, errorCorrectionLevel, format: 'base64', }) return { contents: [ { text: `QR Code for "${decodeURIComponent(content)}"`, uri: uri, }, { uri: uri, blob: result.data, mimeType: 'image/png', }, ], } } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) return { contents: [{ text: `Failed to retrieve resource: ${errorMessage}`, uri: uri }], isError: true, } } }) // Add resource templates list method const resourcesTemplatesListRequestSchema = z.object({ method: z.literal('resources/templates/list'), params: z.object({}), }) server.server.setRequestHandler(resourcesTemplatesListRequestSchema, async () => { // Return a list of available QR code resource templates return { resourceTemplates: [ { name: 'wifi-template', description: 'Generate a QR code that connects devices to a WiFi network', example: 'qrcode://wifi?ssid=MyWiFiNetwork&password=securepassword&encryption=WPA', inputSchema: { type: 'object', properties: { ssid: { type: 'string', description: 'Network name', }, encryption: { type: 'string', enum: ['WPA', 'WEP', 'nopass'], description: 'Encryption type', }, password: { type: 'string', description: 'Network password', }, hidden: { type: 'boolean', description: 'Whether the network is hidden', default: false, }, }, required: ['ssid', 'encryption'], }, }, { name: 'contact-template', description: 'Generate a QR code containing contact information in vCard format', example: 'qrcode://contact?name=Jane%20Doe&phone=555-123-4567&email=jane@example.com', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Contact name', }, phone: { type: 'string', description: 'Phone number', }, email: { type: 'string', description: 'Email address', }, organization: { type: 'string', description: 'Organization or company name', }, title: { type: 'string', description: 'Job title or position', }, url: { type: 'string', description: 'Website URL', }, address: { type: 'string', description: 'Physical address', }, }, required: ['name'], }, }, { name: 'url-template', description: 'Generate a QR code that opens a website or web resource', example: 'qrcode://url?url=https://example.com&size=300&level=H', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to encode', }, size: { type: 'number', description: 'Size of the QR code (100-1000 pixels)', default: 300, }, level: { type: 'string', enum: ['L', 'M', 'Q', 'H'], description: 'Error correction level', default: 'M', }, }, required: ['url'], }, }, { name: 'text-template', description: 'Generate a QR code containing plain text', example: 'qrcode://text?content=This%20is%20a%20sample%20text%20message&size=250&level=Q', inputSchema: { type: 'object', properties: { content: { type: 'string', description: 'Text content to encode', }, size: { type: 'number', description: 'Size of the QR code (100-1000 pixels)', default: 250, }, level: { type: 'string', enum: ['L', 'M', 'Q', 'H'], description: 'Error correction level', default: 'M', }, }, required: ['content'], }, }, { name: 'calendar-template', description: 'Generate a QR code for calendar events in iCalendar format', example: 'qrcode://calendar?title=Team%20Meeting&start=2025-03-15T10:00:00&end=2025-03-15T11:00:00&location=Conference%20Room&description=Weekly%20team%20sync', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Event title', }, start: { type: 'string', description: 'Start time in ISO format (YYYY-MM-DDTHH:MM:SS)', }, end: { type: 'string', description: 'End time in ISO format (YYYY-MM-DDTHH:MM:SS)', }, location: { type: 'string', description: 'Event location', }, description: { type: 'string', description: 'Event description', }, }, required: ['title', 'start'], }, }, ], } }) // Add prompts list method const promptsListRequestSchema = z.object({ method: z.literal('prompts/list'), params: z.object({}), }) server.server.setRequestHandler(promptsListRequestSchema, async () => { // Return a list of example prompts for QR code generation return { prompts: [ { name: 'create-wifi-qr', description: 'Create a QR code for connecting to a WiFi network', prompt: "Generate a QR code that allows people to connect to my home WiFi network called 'MyHomeNetwork' with password 'secure123' using WPA encryption", }, { name: 'create-contact-qr', description: 'Create a QR code with contact information', prompt: 'Create a QR code with my contact information: name: Jane Smith, phone: 555-987-6543, email: jane@example.com, organization: ABC Corp, title: Senior Developer', }, { name: 'create-url-qr', description: 'Create a QR code for a website URL', prompt: 'Make a QR code for my website at https://example.com with high error correction', }, { name: 'create-text-qr', description: 'Create a QR code with plain text', prompt: "Generate a QR code with this text message: 'Thank you for visiting our booth at the conference! Use discount code CONF2025 for 20% off your next purchase.'", }, { name: 'create-calendar-qr', description: 'Create a QR code for a calendar event', prompt: "Create a QR code for our team meeting on March 15, 2025 from 10:00 AM to 11:30 AM in Conference Room A with the description 'Quarterly planning session'", }, { name: 'create-custom-sized-qr', description: 'Create a QR code with custom size settings', prompt: 'Generate a large QR code (size 8) for my business URL https://mybusiness.example.com', }, { name: 'qr-with-high-error-correction', description: 'Create a QR code with high error correction level', prompt: 'Create a QR code for https://example.org with the highest possible error correction level for maximum reliability', }, { name: 'text-output-qr', description: 'Create a QR code with text output format', prompt: "Generate a QR code for 'Hello World' and display it as ASCII/text art that I can paste into a document", }, ], } }) // Export the server instance to be used with a transport export default server

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jwalsh/mcp-server-qrcode'

If you have feedback or need assistance with the MCP directory API, please join our Discord server