Skip to main content
Glama

MCP QR Code Server

by jwalsh
DEVELOPERS.org23 kB
#+TITLE: Developer Guide for MCP QR Code Server * Prerequisites ** System Requirements - Node.js >= 18.0.0 - ~qrencode~ utility installed on your system *** MacOS Installation #+begin_src bash brew install qrencode #+end_src *** Linux (Debian/Ubuntu) Installation #+begin_src bash apt-get install qrencode #+end_src *** Linux (Red Hat/Fedora) Installation #+begin_src bash dnf install qrencode #+end_src * Installation #+begin_src bash # Clone the repository git clone https://github.com/jwalsh/mcp-server-qrcode.git cd mcp-server-qrcode # Install dependencies npm install # Build the server npm run build #+end_src * Development Setup ** Initial Project Setup #+begin_src bash # Initialize the project make initialize # Set up development environment make setup #+end_src ** Running the Project #+begin_src bash # Start in development mode make dev # Quick start the application make quickstart #+end_src * Running the Server #+begin_src bash # Start the MCP server npm start # For local development with CLI features npm run cli -- -g "Hello World" # Test piping with the CLI echo "Hello World" | npm run cli #+end_src The server will start and listen for MCP requests over stdin/stdout. ** Deployment Modes This project supports two modes: 1. *MCP Server Mode (Production)*: When installed globally with ~npm install -g~, the ~mcp-server-qrcode~ command starts the MCP server with no CLI functionality. This is what end users will use with Claude Desktop, MCP Inspector, etc. 2. *CLI Mode (Development)*: For local development and testing, use ~npm run cli~ to access CLI features including stdin piping and command-line arguments. These features are not included in the published package. * Architecture The project consists of two main components: 1. A command-line interface (CLI) for generating QR codes (for development/testing) 2. An MCP server implementation for integration with MCP clients (primary use case) Both components share the core QR code generation functionality defined in ~src/qrcode.ts~, which provides a unified interface for different output formats. ** Command-Line Interface (CLI) The CLI supports multiple modes of operation (primarily for development and testing): 1. *Pipe mode*: Accept input via stdin #+begin_src bash echo "https://example.com" | npm run cli #+end_src 2. *Generate mode*: Generate QR code with command line options #+begin_src bash npm run cli -- -g "https://example.com" -s 300 -e H #+end_src ** MCP Server Implementation The MCP server enables interaction with MCP clients like Claude Desktop and Inspector. It's implemented following the Model Context Protocol standards. *** Core Implementation Structure The ~index.ts~ file contains the primary MCP server implementation: #+begin_src typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; // Create an MCP server const server = new McpServer({ name: "QR Code Generator", version: "0.1.0" }); // Add the QR code generation tool server.tool( "generate-qrcode", { content: z.string().describe("The text content to encode in the QR code"), errorCorrectionLevel: z.enum(["L", "M", "Q", "H"]) .describe("Error correction level") .optional() .default("M"), // Other parameters... }, async ({ content, errorCorrectionLevel, size, format }) => { // Tool implementation... } ); export default server; #+end_src *** Server Entry Point The ~main.ts~ file provides the entry point that connects the server to a transport: #+begin_src typescript import server from './index.js'; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; // Create a stdio transport const transport = new StdioServerTransport(); // Connect the server to the transport server.connect(transport) .catch(error => { console.error('Failed to start server:', error); process.exit(1); }); #+end_src *** Example MCP Server Implementations For more sophisticated MCP server examples and patterns, refer to these implementations: 1. *File System Server*: [[https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/index.ts][filesystem/index.ts]] - Provides access to local files - Example of handling file resources 2. *GitHub Server*: [[https://github.com/modelcontextprotocol/servers/blob/main/src/github/index.ts][github/index.ts]] - Provides access to GitHub repositories - Example of API integration 3. *Google Maps Server*: [[https://github.com/modelcontextprotocol/servers/blob/main/src/google-maps/index.ts][google-maps/index.ts]] - Provides access to Google Maps - Example of API key authentication 4. *Everything Server*: [[https://github.com/modelcontextprotocol/servers/blob/main/src/everything/index.ts][everything/index.ts]] - Desktop search integration - Example of local application integration * Using with MCP Clients ** Claude Desktop #+begin_src json { "mcpServers": { "qrcode": { "args": [ "$HOME/projects/mcp-server-qrcode/build/cli.js" ], "command": "node" } } } #+end_src ** MCP Inspector This server can be used with any MCP-compatible client. Here's how to use it with the MCP Inspector: #+begin_src bash # For MCP Inspector, use the main.js file mcp-inspector -- node build/main.js # Or use the Makefile target make inspector-dev #+end_src For the MCP Inspector UI, use these settings: - Transport Type: STDIO - Command: node - Arguments: build/main.js Important: Always use build/main.js as the entry point for the MCP server when using the inspector. ** Command Line Piping The server also supports direct piping from the command line: #+begin_src bash echo "https://example.com" | mcp-server-qrcode cat myfile.txt | mcp-server-qrcode #+end_src * Debugging - https://modelcontextprotocol.io/docs/tools/debugging * Testing #+begin_src bash # Run tests make test # Run tests with watch make test-watch # Run tests with coverage make test-coverage #+end_src * JSONRPC Method Comparison The following table shows the JSONRPC methods supported across different MCP implementations: | JSONRPC Method | MCP Inspector | mcp.el | QR Code Server | |-----------------------------+---------------+--------------------------------------------------------+----------------| | ~initialize~ | ✅ | ✅ Via ~mcp-async-initialize-message~ | ✅ | | ~ping~ | ✅ | ✅ Via ~mcp-async-ping~ | ✅ | | ~notifications/initialized~ | ✅ | ✅ Via ~mcp-notify~ | ✅ | | ~notifications/stderr~ | ✅ | ❌ | ❌ | | ~tools/list~ | ✅ | ✅ Via ~mcp-async-list-tools~ | ✅ | | ~tools/call~ | ✅ | ✅ Via ~mcp-call-tool~ & ~mcp-async-call-tool~ | ✅ | | ~prompts/list~ | ✅ | ✅ Via ~mcp-async-list-prompts~ | ✅ | | ~prompts/get~ | ✅ | ✅ Via ~mcp-get-prompt~ & ~mcp-async-get-prompt~ | ✅ | | ~resources/list~ | ✅ | ✅ Via ~mcp-async-list-resources~ | ✅ | | ~resources/read~ | ✅ | ✅ Via ~mcp-read-resource~ & ~mcp-async-read-resource~ | ✅ | | ~resources/templates/list~ | ✅ | ✅ Via ~mcp-async-list-resource-templates~ | ✅ | * Interactive Tests :PROPERTIES: :CUSTOM_ID: tests :END: These tests can be run interactively to verify the functionality of the MCP QR Code Server. ** Build Project First :PROPERTIES: :CUSTOM_ID: build :END: #+begin_src bash :tangle scripts/build.sh :mkdirp yes :shebang "#!/bin/bash" # Ensure the project is built before running tests npm run build echo "✅ Project built successfully" #+end_src #+RESULTS: : ✅ Project built successfully ** Core Method Tests :PROPERTIES: :CUSTOM_ID: core-methods :END: *** Initialize Method #+begin_src bash :tangle scripts/test-initialize.sh :mkdirp yes :results output :exports both :results output :exports both :shebang "#!/bin/bash" echo '{"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"roots":{"listChanged":true}},"clientInfo":{"name":"test-client","version":"0.1.0"}},"id":1,"jsonrpc":"2.0"}' | node build/main.js | jq '.' #+end_src #+RESULTS: #+begin_example { "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {} }, "serverInfo": { "name": "QR Code Generator", "version": "0.1.0" } }, "jsonrpc": "2.0", "id": 1 } #+end_example *** Ping Method #+begin_src bash :tangle scripts/test-ping.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"ping","params":{},"id":1,"jsonrpc":"2.0"}' | node build/main.js | jq '.' #+end_src #+RESULTS: #+begin_example { "result": "pong", "jsonrpc": "2.0", "id": 1 } #+end_example ** Tools Interface Tests :PROPERTIES: :CUSTOM_ID: tools-tests :END: *** List Tools #+begin_src bash :tangle scripts/test-tools-list.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"tools/list","params":{},"id":1,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.tools[] | {name, description}' #+end_src #+RESULTS: #+begin_example { "name": "generate-qrcode", "description": "Generate a QR code from text content" } #+end_example *** Call QR Code Generation Tool (Text Format) #+begin_src bash :tangle scripts/test-tools-call-text.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"tools/call","params":{"name":"generate-qrcode","arguments":{"content":"https://anthropic.com","format":"text"}},"id":2,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.content[] | select(.type=="text") | .text' #+end_src #+RESULTS: #+begin_example "QR code generated for: https://anthropic.com" "██████████████████████████████████████████████\n██████████████████████████████████████████████\n████ ██████ ████\n████ ██████████ ██████ ██████████ ████\n████ ██ ██ ██████ ██ ██ ████\n████ ██ ██ ██████ ██ ██ ████\n████ ██ ██ ██████ ██ ██ ████\n████ ██████████ ██████ ██████████ ████\n████ ██████ ████\n████████████ ██ ██ ██ ██ ██ ██████\n██████████ ██ ██ ██████ ██████\n██████████ ████ ████ ██████\n██████████ ██ ██ ██ ██ ████ ████\n██████████ ██████ ██ ██ ██ ██████\n████ ██ ██████ ██████\n████ ██████████ ████ ████ ██ ██████\n████ ██ ██ ██ ██████ ████████\n████ ██ ██ ██████ ██ ██ ██████\n████ ██ ██ ██ ██ ██ ██ ████████\n████ ██████████ ██ ██ ██ ██████\n████ ██ ██ ██ ██████████\n██████████████████████████████████████████████\n██████████████████████████████████████████████" #+end_example *** Call QR Code Generation Tool (Image Format) #+begin_src bash :tangle scripts/test-tools-call-image.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" # This will generate a base64-encoded image - show just the first part echo '{"method":"tools/call","params":{"name":"generate-qrcode","arguments":{"content":"https://anthropic.com","format":"image"}},"id":2,"jsonrpc":"2.0"}' | node build/main.js | jq -r '.result.content[] | select(.type=="image") | .data' | head -c 50 echo "... [base64 data truncated]" #+end_src #+RESULTS: : iVBORw0KGgoAAAANSUhEUgAAAhwAAAIcAQMAAACSSQR3AAA... [base64 data truncated] ** Prompts Interface Tests :PROPERTIES: :CUSTOM_ID: prompts-tests :END: *** List Prompts #+begin_src bash :tangle scripts/test-prompts-list.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"prompts/list","params":{},"id":1,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.prompts' #+end_src #+RESULTS: #+begin_example [ { "name": "qrcode-formatter", "description": "Format QR code input for various use cases", "inputSchema": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "url", "wifi", "contact", "text" ], "description": "Type of QR code content to format" }, "parameters": { "type": "object", "description": "Parameters specific to the QR code type" } }, "required": [ "type", "parameters" ] } } ] #+end_example *** Get Prompt (Wi-Fi) #+begin_src bash :tangle scripts/test-prompts-get-wifi.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"prompts/get","params":{"name":"qrcode-formatter","arguments":{"type":"wifi","parameters":{"ssid":"GuestWiFi","password":"Welcome123","encryption":"WPA"}}},"id":2,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.content' #+end_src #+RESULTS: : "WIFI:S:GuestWiFi;T:WPA;P:Welcome123;;" ** Resources Interface Tests :PROPERTIES: :CUSTOM_ID: resources-tests :END: *** List Resources #+begin_src bash :tangle scripts/test-resources-list.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"resources/list","params":{},"id":1,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.resources' #+end_src #+RESULTS: #+begin_example [ { "uri": "qrcode-examples", "description": "Examples of different QR code formats", "mimeType": "text/markdown" }, { "uri": "qrcode-specs", "description": "Technical specifications for QR code generation", "mimeType": "text/markdown" } ] #+end_example *** Read Resource #+begin_src bash :tangle scripts/test-resources-read.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"resources/read","params":{"uri":"qrcode-examples"},"id":2,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.content' #+end_src #+RESULTS: : "# QR Code Examples\n\n## URL\n`https://example.com`\n\n## Wi-Fi\n`WIFI:S:MyNetwork;T:WPA;P:MyPassword;;`\n\n## Contact\n```\nBEGIN:VCARD\nN:Doe;John;;;\nFN:John Doe\nEMAIL:john@example.com\nTEL:555-123-4567\nEND:VCARD\n```" *** List Resource Templates #+begin_src bash :tangle scripts/test-resources-templates-list.sh :mkdirp yes :results output :exports both :shebang "#!/bin/bash" echo '{"method":"resources/templates/list","params":{},"id":3,"jsonrpc":"2.0"}' | node build/main.js | jq '.result.resourceTemplates' #+end_src #+RESULTS: #+begin_example [ { "name": "wifi-template", "description": "Template for Wi-Fi QR codes", "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" ] } } ] #+end_example * JSON Schema Reference :PROPERTIES: :CUSTOM_ID: json-schemas :END: ** Core Methods *** ~initialize~ Request/Response Schema #+begin_src js :tangle schemas/initialize.js :mkdirp yes :results output :exports both // initialize request { "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": { "roots": { "listChanged": true } }, "clientInfo": { "name": "mcp-client-name", "version": "0.1.0" } }, "id": 1, "jsonrpc": "2.0" } // initialize response { "result": { "protocolVersion": "2024-11-05", "serverInfo": { "name": "mcp-server-qrcode", "version": "0.1.0" }, "capabilities": { "tools": {}, "prompts": {}, "resources": {} } }, "id": 1, "jsonrpc": "2.0" } #+end_src *** ~tools/call~ Request/Response Schema #+begin_src js :tangle schemas/tools-call.js :mkdirp yes :results output :exports both // tools/call request for QR code generation { "method": "tools/call", "params": { "name": "generate-qrcode", "arguments": { "content": "https://example.com", "format": "image", "errorCorrectionLevel": "M", "size": 3 } }, "id": 2, "jsonrpc": "2.0" } // tools/call response { "result": { "content": [ { "type": "text", "text": "QR code generated for: https://example.com" }, { "type": "image", "mimeType": "image/png", "data": "base64-encoded-image-data" } ] }, "id": 2, "jsonrpc": "2.0" } #+end_src *** ~prompts/get~ Request/Response Schema #+begin_src js :tangle schemas/prompts-get.js :mkdirp yes :results output :exports both // prompts/get request for Wi-Fi { "method": "prompts/get", "params": { "name": "qrcode-formatter", "arguments": { "type": "wifi", "parameters": { "ssid": "MyWiFi", "password": "MyPassword", "encryption": "WPA" } } }, "id": 2, "jsonrpc": "2.0" } // prompts/get response { "result": { "content": "WIFI:S:MyWiFi;T:WPA;P:MyPassword;;" }, "id": 2, "jsonrpc": "2.0" } #+end_src * References ** MCP Protocol Documentation - [[https://modelcontextprotocol.io/docs/specification/jsonrpc][MCP JSONRPC Specification]] - [[https://modelcontextprotocol.io/docs/tools/mcp-inspector][MCP Inspector Usage]] - [[https://modelcontextprotocol.io/docs/tools/debugging][MCP Debugging Guide]] ** Example MCP Server Implementations - [[https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/index.ts][File System Server]] - [[https://github.com/modelcontextprotocol/servers/blob/main/src/github/index.ts][GitHub Server]] - [[https://github.com/modelcontextprotocol/servers/blob/main/src/google-maps/index.ts][Google Maps Server]] * Linting and Formatting #+begin_src bash # Lint code make lint # Automatically fix linting issues make lint-fix # Format code make format #+end_src * Continuous Integration #+begin_src bash # Run all checks (lint, format, typecheck, test) make ci #+end_src * Release Process ** Release Workflow Follow these steps to release a new version of the package: *** Automated Release Process For the easiest release experience, use the provided release script: #+begin_src bash # For patch releases (default) ./scripts/release.sh # For minor releases ./scripts/release.sh minor # For major releases ./scripts/release.sh major #+end_src The script handles most of the process but pauses for manual npm publish (due to 2FA) and final verification. *** Manual Release Process If you need to execute the release steps manually: 1. *Update Version*: Bump the version in package.json #+begin_src bash # Use npm version to update package.json (creates a commit and tag automatically) npm version patch -m "chore: bump version to %s" # or minor, major #+end_src 2. *Generate Changelog*: Update the CHANGELOG.org file #+begin_src bash npm run changelog #+end_src 3. *Commit Changelog*: Commit the changelog updates with [skip ci] #+begin_src bash # CRITICAL: Never use GPG signing for commits in this repo git add CHANGELOG.org git commit --no-gpg-sign -m "docs: update CHANGELOG.org for new version [skip ci]" #+end_src 4. *Push Changes*: Push the commits and tags #+begin_src bash git push origin main git push origin --tags #+end_src 5. *Create GitHub Release*: Create a release with notes from the changelog #+begin_src bash # Generate release tarball npm pack # Create draft release with the tarball VERSION=$(jq -r .version package.json) gh release create "v$VERSION" *.tgz --title "v$VERSION" --notes "See CHANGELOG.org for details" --draft #+end_src 6. *Pre-Release Verification*: Test with MCP Inspector before publishing #+begin_src bash # Verify server functionality with MCP Inspector make inspector-dev # Test in the inspector UI by creating a QR code #+end_src 7. *Publish to npm*: Publish the package (requires 2FA) #+begin_src bash npm publish #+end_src 8. *Verify Publication*: Confirm the package is properly published #+begin_src bash # Verify npm package npm view @jwalsh/mcp-server-qrcode version #+end_src 9. *Publish GitHub Release*: Remove draft status from the release #+begin_src bash gh release edit "v$VERSION" --draft=false #+end_src When validating with MCP Inspector, the successful connection should look like this: [[file:static/localhost_5173_.png][MCP Inspector Validation]] * Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Run ~make ci~ to ensure all checks pass 5. Submit a pull request * Troubleshooting - Ensure you're using Node.js 18.0.0 or higher - Install ~qrencode~ utility for your system - Run ~make initialize~ if you encounter dependency issues - Check ~make setup~ for environment verification

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