Skip to main content
Glama

MCP QR Code Server

by jwalsh
cli.ts10.8 kB
#!/usr/bin/env node import server from './index.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { program } from 'commander' import { generateQRCode } from './qrcode.js' import { writeFile } from 'fs/promises' import { promisify } from 'util' import { exec } from 'child_process' import path from 'path' const execAsync = promisify(exec) // Check if we have stdin data (piped input) const hasStdin = !process.stdin.isTTY // Configure the main CLI program.name('mcp-server-qrcode').description('MCP server for generating QR codes').version('0.3.6') // Generate command program .command('generate') .description('Generate a QR code') .option('-c, --content <content>', 'Content to encode in the QR code') .option('-s, --size <size>', 'Size of the QR code (100-1000 pixels)', '200') .option('-e, --error-correction <level>', 'Error correction level (L, M, Q, H)', 'M') .option('-o, --output <file>', 'Output file path (defaults to terminal output)') .option('-f, --format <format>', 'Output format (text or image)', 'text') .action(async options => { try { const content = options.content if (!content) { console.error('Error: No content provided for QR code generation') process.exit(1) } const size = parseInt(options.size, 10) const format = options.format === 'image' ? 'base64' : 'terminal' // Generate QR code const result = await generateQRCode({ content, size, errorCorrectionLevel: options.errorCorrection as 'L' | 'M' | 'Q' | 'H', format, }) // Handle output if (options.output) { if (format === 'base64') { // Write image to file const buffer = Buffer.from(result.data, 'base64') await writeFile(options.output, buffer) console.log(`QR code image saved to ${options.output}`) } else { // Write text to file await writeFile(options.output, result.data) console.log(`QR code text saved to ${options.output}`) } } else { // Output to terminal console.log(result.data) } } catch (error) { console.error( 'Error generating QR code:', error instanceof Error ? error.message : String(error) ) process.exit(1) } }) // Resource command program .command('resource <uri>') .description('Generate QR code from MCP resource URI') .option('-o, --output <file>', 'Output file path (defaults to terminal display)') .option('-v, --view', 'View the generated QR code image immediately (if supported)') .action(async (uri, options) => { try { // Validate URI format if (!uri.startsWith('qrcode://')) { console.error('Error: Resource URI must start with qrcode://') console.log('Examples:') console.log(' qrcode://sample') console.log(' qrcode://hello-world') console.log(' qrcode://https://example.com?size=300&level=H') process.exit(1) } // Determine resource type and parameters let resourceUri = uri let isSampleResource = false let content = '' let size = 200 let errorCorrectionLevel: 'L' | 'M' | 'Q' | 'H' = 'M' if (uri === 'qrcode://sample') { isSampleResource = true } else { // Custom resource - Extract content and parameters from the URI const contentMatch = uri.match(/^qrcode:\/\/(.+?)(?:\?|$)/) content = contentMatch ? contentMatch[1] : '' // 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 directly based on requested resource let result: { contents: { text: string; uri: string; blob: string; mimeType: string }[] isError?: boolean } if (isSampleResource) { // Sample QR code const qrResult = await generateQRCode({ content: 'https://github.com/jwalsh/mcp-server-qrcode', size: 200, errorCorrectionLevel: 'M', format: 'base64', }) // Create a formatted result mimicking the resource API result = { contents: [ { text: 'Sample QR Code for MCP QR Code Server', uri: 'qrcode://sample', blob: qrResult.data, mimeType: 'image/png', }, ], } } else { // Custom QR code if (!content) { console.error('Error: Content parameter is required') process.exit(1) } try { // Generate QR code const qrResult = await generateQRCode({ content: decodeURIComponent(content), size, errorCorrectionLevel, format: 'base64', }) // Create a formatted result mimicking the resource API result = { contents: [ { text: `QR Code for "${decodeURIComponent(content)}"`, uri: resourceUri, blob: qrResult.data, mimeType: 'image/png', }, ], } } catch (error) { console.error( 'Error generating QR code:', error instanceof Error ? error.message : String(error) ) process.exit(1) } } if (result.isError) { console.error('Error:', result.contents[0].text) process.exit(1) } // Find image content in the result const imageContent = result.contents.find( item => item.blob && item.mimeType?.includes('image') ) if (!imageContent) { console.error('Error: No image content found in resource result') process.exit(1) } // Handle output options if (options.output) { // Write image to file const buffer = Buffer.from(imageContent.blob, 'base64') await writeFile(options.output, buffer) console.log(`QR code image saved to ${options.output}`) // Open image if view flag is set if (options.view) { try { const absolutePath = path.resolve(options.output) // Try to open with different commands based on platform if (process.platform === 'darwin') { await execAsync(`open "${absolutePath}"`) } else if (process.platform === 'win32') { await execAsync(`start "" "${absolutePath}"`) } else { await execAsync(`xdg-open "${absolutePath}"`) } } catch (error) { console.error( 'Failed to open image:', error instanceof Error ? error.message : String(error) ) } } } else { // No output file, print information console.log('QR Code generated successfully.') console.log('Resource URI:', uri) console.log('Use --output flag to save the QR code as an image file.') } } catch (error) { console.error( 'Error generating resource:', error instanceof Error ? error.message : String(error) ) process.exit(1) } }) // Server command (default) program .command('server') .description('Start the QR Code MCP server (default if no command is specified)') .option('-v, --verbose', 'Enable verbose logging') .action(async options => { try { const verbose = options.verbose || false console.log(`Starting QR Code MCP server with verbose: ${verbose}`) // Create a stdio transport for the server const transport = new StdioServerTransport() // Connect the server to the transport await server.connect(transport) console.log('QR Code MCP server is running') } catch (error) { console.error( 'Failed to start server:', error instanceof Error ? error.message : String(error) ) process.exit(1) } }) // Handle stdin piping if (hasStdin) { let inputData = '' process.stdin.on('data', chunk => { inputData += chunk }) process.stdin.on('end', async () => { try { const content = inputData.trim() if (!content) { console.error('Error: No content provided for QR code generation') process.exit(1) } // Generate QR code in terminal-friendly format const result = await generateQRCode({ content, size: 200, errorCorrectionLevel: 'M', format: 'terminal', }) // Output the QR code to the terminal console.log(result.data) } catch (error) { console.error( 'Error generating QR code:', error instanceof Error ? error.message : String(error) ) process.exit(1) } }) } else { // Handle older CLI interface for backward compatibility program .option('-v, --verbose', 'Enable verbose logging') .option('-g, --generate <content>', 'Generate QR code from the provided content') .option('-s, --size <size>', 'Size of the QR code (100-1000 pixels)', '200') .option('-e, --error-correction <level>', 'Error correction level (L, M, Q, H)', 'M') // Support original behavior for backward compatibility program.action(async options => { // Backward compatibility for old CLI interface if (options.generate) { const result = await generateQRCode({ content: options.generate, size: parseInt(options.size, 10), errorCorrectionLevel: options.errorCorrection as 'L' | 'M' | 'Q' | 'H', format: 'terminal', }) console.log(result.data) } else if (!program.args.length || program.args[0] === 'server') { // Start in server mode if no other command or specifically server command const verbose = options.verbose || false console.log(`Starting QR Code MCP server with verbose: ${verbose}`) // Create a stdio transport for the server const transport = new StdioServerTransport() // Connect the server to the transport await server.connect(transport) console.log('QR Code MCP server is running') } }) // Parse command line arguments program.parse() }

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