Skip to main content
Glama
kesslerio

YOURLS-MCP

by kesslerio

generate_qr_code

Create QR codes for shortened URLs with customizable size, format, and margin. Integrates with YOURLS-MCP to simplify sharing and tracking.

Instructions

Generate a QR code for a shortened URL

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
formatNoImage format (png, svg, etc.)
marginNoMargin size
shorturlYesThe short URL to generate a QR code for
sizeNoSize of the QR code in pixels

Implementation Reference

  • The main handler function (execute) for the generate_qr_code MCP tool. Validates inputs, calls yourlsClient.generateQrCode, and formats the MCP response.
    execute: async ({ shorturl, size, border, ecc, format }) => {
      try {
        // Validate size if provided
        if (size !== undefined) {
          const sizeNum = Number(size);
          if (isNaN(sizeNum) || sizeNum <= 0) {
            throw new Error('Size must be a positive number');
          }
          
          // Add upper limit check for QR code size
          if (sizeNum > 1000) {
            throw new Error('QR code size cannot exceed 1000 pixels for performance reasons');
          }
        }
        
        // Validate border if provided
        if (border !== undefined) {
          const borderNum = Number(border);
          if (isNaN(borderNum) || borderNum < 0) {
            throw new Error('Border must be a non-negative number');
          }
        }
        
        // Validate ecc if provided
        if (ecc && !['L', 'M', 'Q', 'H'].includes(ecc.toUpperCase())) {
          throw new Error(`Error correction level '${ecc}' is not supported. Must be one of: L (low), M (medium), Q (quartile), or H (high)`);
        }
        
        // Normalize ecc to uppercase
        if (ecc) {
          ecc = ecc.toUpperCase();
        }
        
        // Validate format if provided
        if (format && !['png', 'jpg', 'jpeg', 'gif', 'svg'].includes(format.toLowerCase())) {
          throw new Error(`Format '${format}' is not supported. Must be one of: png, jpg, jpeg, gif, svg`);
        }
        
        // Define MIME type mapping to ensure consistency
        const formatToMimeType = {
          'png': 'image/png',
          'jpg': 'image/jpeg',
          'jpeg': 'image/jpeg',
          'svg': 'image/svg+xml',
          'gif': 'image/gif'
        };
        
        // Generate QR code
        const result = await yourlsClient.generateQrCode(shorturl, {
          size: size !== undefined ? Number(size) : undefined,
          border: border !== undefined ? Number(border) : undefined,
          ecc,
          format
        });
        
        if (result.status === 'success') {
          return createMcpResponse(true, {
            shorturl: shorturl,
            data: result.data,
            contentType: result.contentType,
            url: result.url,
            config: result.config
          });
        } else {
          throw new Error(result.message || 'Unknown error');
        }
      } catch (error) {
        return createMcpResponse(false, {
          message: error.message,
          shorturl: shorturl
        });
      }
    }
  • Input schema definition for the generate_qr_code tool, defining parameters and validation rules.
    inputSchema: {
      type: 'object',
      properties: {
        shorturl: {
          type: 'string',
          description: 'The short URL or keyword to generate a QR code for'
        },
        size: {
          type: 'number',
          description: 'QR code size in pixels (default: depends on plugin configuration)'
        },
        border: {
          type: 'number',
          description: 'Border width around the QR code (default: depends on plugin configuration)'
        },
        ecc: {
          type: 'string',
          description: 'Error correction level: L (low), M (medium), Q (quartile), or H (high)'
        },
        format: {
          type: 'string',
          description: 'Image format (e.g., png, jpg, svg)'
        }
      },
      required: ['shorturl']
    },
  • src/index.js:242-252 (registration)
    Registration of the generate_qr_code tool on the MCP server in the main entry point.
    server.tool(
      generateQrCodeTool.name,
      generateQrCodeTool.description,
      {
        shorturl: z.string().describe('The short URL to generate a QR code for'),
        size: z.number().optional().describe('Size of the QR code in pixels'),
        format: z.string().optional().describe('Image format (png, svg, etc.)'),
        margin: z.number().optional().describe('Margin size')
      },
      generateQrCodeTool.execute
    );
  • Underlying YOURLS API client method that generates the QR code image by calling the YOURLS-IQRCodes plugin endpoint.
    async generateQrCode(shorturl, { size, border, ecc, format } = {}) {
      // Define MIME type mapping to ensure consistency
      const formatToMimeType = {
        'png': 'image/png',
        'jpg': 'image/jpeg',
        'jpeg': 'image/jpeg',
        'svg': 'image/svg+xml',
        'gif': 'image/gif'
      };
      
      try {
        // First check if the shorturl exists
        await this.expand(shorturl);
        
        // Construct the QR code URL (YOURLS-IQRCodes plugin appends .qr to the shorturl)
        const baseUrl = this.api_url.replace('yourls-api.php', '');
        let qrUrl = `${baseUrl}${shorturl}.qr`;
        
        // Validate and normalize the parameters
        let normalizedSize, normalizedBorder, normalizedEcc, normalizedFormat;
        
        // Validate size
        if (size !== undefined) {
          normalizedSize = Number(size);
          if (isNaN(normalizedSize) || normalizedSize <= 0) {
            throw new Error('Size must be a positive number');
          }
          if (normalizedSize > 1000) {
            throw new Error('QR code size cannot exceed 1000 pixels for performance reasons');
          }
        }
        
        // Validate border
        if (border !== undefined) {
          normalizedBorder = Number(border);
          if (isNaN(normalizedBorder) || normalizedBorder < 0) {
            throw new Error('Border must be a non-negative number');
          }
        }
        
        // Validate and normalize ECC
        if (ecc !== undefined) {
          if (!['L', 'M', 'Q', 'H', 'l', 'm', 'q', 'h'].includes(ecc)) {
            throw new Error(`Error correction level '${ecc}' is not supported. Must be one of: L, M, Q, H`);
          }
          normalizedEcc = ecc.toUpperCase();
        }
        
        // Validate and normalize format
        if (format !== undefined) {
          normalizedFormat = format.toLowerCase();
          if (!Object.keys(formatToMimeType).includes(normalizedFormat)) {
            throw new Error(`Format '${format}' is not supported. Must be one of: png, jpg, jpeg, gif, svg`);
          }
        }
        
        // Add query parameters if provided
        const params = new URLSearchParams();
        if (normalizedSize !== undefined) params.append('size', normalizedSize);
        if (normalizedBorder !== undefined) params.append('border', normalizedBorder);
        if (normalizedEcc !== undefined) params.append('ecc', normalizedEcc);
        if (normalizedFormat !== undefined) params.append('format', normalizedFormat);
        
        const queryString = params.toString();
        if (queryString) {
          qrUrl += `?${queryString}`;
        }
        
        // Fetch the QR code image
        const response = await axios.get(qrUrl, {
          responseType: 'arraybuffer'
        });
        
        // Convert to base64
        const base64Image = Buffer.from(response.data, 'binary').toString('base64');
        
        // Determine MIME type - use our mapping if we know the format, otherwise use server-provided
        const contentType = normalizedFormat ? 
          formatToMimeType[normalizedFormat] : 
          (response.headers['content-type'] || 'image/png');
        
        return {
          status: 'success',
          data: base64Image,
          contentType: contentType,
          url: qrUrl,
          config: {
            size: normalizedSize,
            border: normalizedBorder,
            ecc: normalizedEcc,
            format: normalizedFormat
          }
        };
      } catch (error) {
        // If the error is from the expand method (meaning the shorturl doesn't exist)
        if (error.response && error.response.status === 404) {
          throw new Error(`The short URL or keyword '${shorturl}' was not found in the database.`);
        }
        
        // If the error is from fetching the QR code (meaning the IQRCodes plugin isn't installed)
        if (error.response && 
            (error.response.status === 404 || 
             error.response.status === 500 || 
             error.response.status === 501)) {
          throw new Error('QR code generation is not available. Please install the YOURLS-IQRCodes plugin.');
        }
        
        // For other errors, just throw them
        throw error;
      }
    }
  • Alternative registration of the tool in the tools index module (not used in main entry).
    server.tool(
      generateQrCodeTool.name,
      generateQrCodeTool.description,
      {
        shorturl: z.string().describe('The short URL to generate a QR code for'),
        size: z.number().optional().describe('Size of the QR code in pixels'),
        format: z.string().optional().describe('Image format (png, svg, etc.)'),
        margin: z.number().optional().describe('Margin size')
      },
      generateQrCodeTool.execute
    );
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states what the tool does but doesn't cover key aspects like whether it's a read-only or mutation operation, authentication requirements, rate limits, or error handling. This is inadequate for a tool that likely involves external resources.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without unnecessary words. It's front-loaded and appropriately sized, making it easy to understand at a glance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (generating QR codes with multiple parameters) and lack of annotations and output schema, the description is incomplete. It doesn't explain what the output looks like (e.g., image data or URL), potential side effects, or how it integrates with sibling tools, leaving significant gaps for an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description implies the 'shorturl' parameter is required, but doesn't add meaning beyond the input schema, which has 100% coverage and already describes all parameters clearly. Since schema coverage is high, the baseline score of 3 is appropriate, as the description doesn't compensate with extra details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'generate' and the resource 'QR code', specifying it's for a 'shortened URL'. This distinguishes it from general QR code generators, though it doesn't explicitly differentiate from sibling tools like 'shorten_url' or 'url_analytics' that might also involve URLs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, such as whether it's for generating QR codes specifically for URLs shortened by this service or any shortened URL. It lacks context on prerequisites or exclusions, leaving usage ambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

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/kesslerio/yourls-mcp'

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