convert
Transform images into ASCII art from URLs or base64 sources. Generate customizable text artwork with size tiers (16/32/64), brightness inversion, and structured storage options.
Instructions
Convert an image (URL or base64) to ASCII art at a specific size tier.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | Image URL to convert | |
| base64 | No | Base64-encoded image data | |
| size | No | Art size tier: "16" (simple, default), "32" (medium), "64" (detailed) | 16 |
| invert | No | Invert brightness | |
| contrast | No | Apply auto-contrast | |
| gamma | No | Gamma correction | |
| save | No | Save the converted art to the store |
Implementation Reference
- src/converter.ts:14-72 (handler)The main convertImage function that converts image buffers to ASCII art. Handles resizing, grayscale conversion, contrast normalization, gamma correction, and brightness inversion. Uses sharp library for image processing and maps pixel brightness to ASCII characters using a ramp.
export async function convertImage( input: Buffer, opts: ConvertOptions = {}, ): Promise<string> { const maxW = opts.width ?? 64; const maxH = opts.height ?? 32; const invert = opts.invert ?? false; const contrast = opts.contrast ?? true; const gamma = opts.gamma ?? 1.0; const instance = sharp(input); const metadata = await instance.metadata(); const origW = metadata.width ?? maxW; const origH = metadata.height ?? maxH; const aspect = origH / origW; let newW = maxW; let newH = Math.floor(newW * aspect * 0.5); if (newH > maxH) { newH = maxH; newW = Math.floor(newH / aspect * 2); } newW = Math.max(newW, 1); newH = Math.max(newH, 1); let pipeline = instance.flatten({ background: '#ffffff' }).grayscale(); if (contrast) { pipeline = pipeline.normalise(); } const { data } = await pipeline .resize(newW, newH, { fit: 'fill', kernel: 'lanczos3' }) .raw() .toBuffer({ resolveWithObject: true }); const lines: string[] = []; for (let y = 0; y < newH; y++) { let row = ''; for (let x = 0; x < newW; x++) { let brightness = data[y * newW + x]; if (invert) { brightness = 255 - brightness; } const normalized = Math.pow(brightness / 255, gamma); const idx = Math.min(Math.floor(normalized * RAMP_LEN), RAMP_LEN); row += ASCII_RAMP[idx]; } lines.push(row.trimEnd()); } while (lines.length > 0 && lines[lines.length - 1].trim() === '') { lines.pop(); } return lines.join('\n'); } - src/mcp.ts:180-232 (registration)Registration of the 'convert' tool with the MCP server. Defines the tool name, description, input schema (url, base64, size, invert, contrast, gamma, save), and the handler function that orchestrates image resolution and conversion.
server.tool( 'convert', 'Convert an image (URL or base64) to ASCII art at a specific size tier.', { url: z.string().url().optional().describe('Image URL to convert'), base64: z.string().optional().describe('Base64-encoded image data'), size: sizeSchema, invert: z.boolean().default(false).describe('Invert brightness'), contrast: z.boolean().default(true).describe('Apply auto-contrast'), gamma: z.number().min(0.1).max(5).default(1.0).describe('Gamma correction'), save: z.object({ name: z.string().max(MAX_NAME_LENGTH), description: z.string().max(MAX_DESCRIPTION_LENGTH).optional(), category: z.string().max(MAX_NAME_LENGTH), tags: z.array(z.string().max(MAX_TAG_LENGTH)).max(MAX_TAGS), }).optional().describe('Save the converted art to the store'), }, async ({ url, base64, size, invert, contrast, gamma, save }) => { if (!url && !base64) { return { content: [{ type: 'text', text: 'Error: provide either "url" or "base64"' }], isError: true }; } try { const s = Number(size) as ArtSize; const { width: maxW, height: maxH } = SIZE_LIMITS[s]; const source = (url ?? base64)!; const buf = await resolveImageInput(source); const art = await convertImage(buf, { width: maxW, height: maxH, invert, contrast, gamma }); const lines = art.split('\n'); const artWidth = Math.max(...lines.map((l) => l.length), 0); const artHeight = lines.length; let savedMsg = ''; if (save) { const entry = await addArt({ name: save.name, description: save.description, category: save.category.toLowerCase(), tags: save.tags, size: s, art, }); savedMsg = `\n\nSaved as "${entry.id}" [${entry.size}w ${entry.width}x${entry.height}]`; } const text = `--- ${s}w [${artWidth}x${artHeight}] ---\n${art}${savedMsg}`; return { content: [{ type: 'text', text }] }; } catch (err: unknown) { const e = err as { message?: string }; return { content: [{ type: 'text', text: `Error: ${e.message ?? 'Conversion failed'}` }], isError: true }; } } ); - src/mcp.ts:183-196 (schema)Input schema definition for the convert tool using Zod. Defines validation rules for url (optional URL string), base64 (optional string), size (enum '16'|'32'|'64'), invert/contrast (booleans), gamma (number 0.1-5), and optional save object with name, description, category, and tags.
{ url: z.string().url().optional().describe('Image URL to convert'), base64: z.string().optional().describe('Base64-encoded image data'), size: sizeSchema, invert: z.boolean().default(false).describe('Invert brightness'), contrast: z.boolean().default(true).describe('Apply auto-contrast'), gamma: z.number().min(0.1).max(5).default(1.0).describe('Gamma correction'), save: z.object({ name: z.string().max(MAX_NAME_LENGTH), description: z.string().max(MAX_DESCRIPTION_LENGTH).optional(), category: z.string().max(MAX_NAME_LENGTH), tags: z.array(z.string().max(MAX_TAG_LENGTH)).max(MAX_TAGS), }).optional().describe('Save the converted art to the store'), }, - src/converter.ts:6-12 (schema)TypeScript interface defining the options for convertImage function. Specifies optional width, height, invert, contrast, and gamma parameters for image conversion configuration.
export interface ConvertOptions { width?: number; height?: number; invert?: boolean; contrast?: boolean; gamma?: number; } - src/converter.ts:1-4 (helper)Supporting constants for the convert tool - the ASCII character ramp used to map brightness levels to characters, from darkest (space) to lightest (@).
import sharp from 'sharp'; const ASCII_RAMP = ' .:-=+*#%@'; const RAMP_LEN = ASCII_RAMP.length - 1;