Skip to main content
Glama
simen

VICE C64 Emulator MCP Server

by simen

renderScreen

Convert the C64 emulator screen to ASCII art for visual debugging. Generates a character-based representation of display content using configurable width, height, and character sets.

Instructions

Render the current screen as ASCII art representation.

Creates a visual representation of the screen using ASCII characters to approximate the colors and content visible on the C64 display.

This is useful for quick visual debugging without image handling. For actual screen text, use readScreen instead.

Options:

  • width: Output width in characters (default: 80)

  • height: Output height in lines (default: 50)

  • charset: Character set to use for shading (default: " .:-=+*#%@")

Related tools: readScreen, screenshot, readVicState

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
widthNoOutput width in characters (default: 80)
heightNoOutput height in lines (default: 50)
charsetNoCharacters for shading from dark to light (default: ' .:-=+*#%@')

Implementation Reference

  • Handler function that implements the renderScreen tool: fetches display buffer and palette from VICE, computes pixel luminance, scales and maps to ASCII characters for visual representation.
      try {
        const display = await client.getDisplay();
        const palette = await client.getPalette();
    
        const outputWidth = args.width || 80;
        const outputHeight = args.height || 50;
        const charset = args.charset || " .:-=+*#%@";
    
        // Calculate luminance for each palette color
        const luminance = palette.map((c) => {
          // Standard luminance formula
          return 0.299 * c.r + 0.587 * c.g + 0.114 * c.b;
        });
    
        // Sample the display and convert to ASCII
        const scaleX = display.innerWidth / outputWidth;
        const scaleY = display.innerHeight / outputHeight;
    
        const lines: string[] = [];
    
        for (let y = 0; y < outputHeight; y++) {
          let line = "";
          for (let x = 0; x < outputWidth; x++) {
            // Sample pixel from display
            const srcX = Math.floor(display.offsetX + x * scaleX);
            const srcY = Math.floor(display.offsetY + y * scaleY);
            const pixelIndex = srcY * display.width + srcX;
    
            if (pixelIndex < display.pixels.length) {
              const colorIndex = display.pixels[pixelIndex];
              const lum = colorIndex < luminance.length ? luminance[colorIndex] : 0;
    
              // Map luminance (0-255) to charset index
              const charIndex = Math.floor((lum / 256) * charset.length);
              line += charset[Math.min(charIndex, charset.length - 1)];
            } else {
              line += " ";
            }
          }
          lines.push(line);
        }
    
        return formatResponse({
          width: outputWidth,
          height: outputHeight,
          sourceWidth: display.innerWidth,
          sourceHeight: display.innerHeight,
          charset,
          render: lines.join("\n"),
          hint: `ASCII rendering of ${display.innerWidth}x${display.innerHeight} display scaled to ${outputWidth}x${outputHeight}`,
        });
      } catch (error) {
        return formatError(error as ViceError);
      }
    }
  • src/index.ts:1586-1665 (registration)
    Registers the renderScreen MCP tool with detailed description, Zod input schema for width/height/charset parameters, and references the handler function.
    server.registerTool(
      "renderScreen",
      {
        description: `Render the current screen as ASCII art representation.
    
    Creates a visual representation of the screen using ASCII characters
    to approximate the colors and content visible on the C64 display.
    
    This is useful for quick visual debugging without image handling.
    For actual screen text, use readScreen instead.
    
    Options:
    - width: Output width in characters (default: 80)
    - height: Output height in lines (default: 50)
    - charset: Character set to use for shading (default: " .:-=+*#%@")
    
    Related tools: readScreen, screenshot, readVicState`,
        inputSchema: z.object({
          width: z.number().min(20).max(200).optional().describe("Output width in characters (default: 80)"),
          height: z.number().min(10).max(100).optional().describe("Output height in lines (default: 50)"),
          charset: z.string().optional().describe("Characters for shading from dark to light (default: ' .:-=+*#%@')"),
        }),
      },
      async (args) => {
        try {
          const display = await client.getDisplay();
          const palette = await client.getPalette();
    
          const outputWidth = args.width || 80;
          const outputHeight = args.height || 50;
          const charset = args.charset || " .:-=+*#%@";
    
          // Calculate luminance for each palette color
          const luminance = palette.map((c) => {
            // Standard luminance formula
            return 0.299 * c.r + 0.587 * c.g + 0.114 * c.b;
          });
    
          // Sample the display and convert to ASCII
          const scaleX = display.innerWidth / outputWidth;
          const scaleY = display.innerHeight / outputHeight;
    
          const lines: string[] = [];
    
          for (let y = 0; y < outputHeight; y++) {
            let line = "";
            for (let x = 0; x < outputWidth; x++) {
              // Sample pixel from display
              const srcX = Math.floor(display.offsetX + x * scaleX);
              const srcY = Math.floor(display.offsetY + y * scaleY);
              const pixelIndex = srcY * display.width + srcX;
    
              if (pixelIndex < display.pixels.length) {
                const colorIndex = display.pixels[pixelIndex];
                const lum = colorIndex < luminance.length ? luminance[colorIndex] : 0;
    
                // Map luminance (0-255) to charset index
                const charIndex = Math.floor((lum / 256) * charset.length);
                line += charset[Math.min(charIndex, charset.length - 1)];
              } else {
                line += " ";
              }
            }
            lines.push(line);
          }
    
          return formatResponse({
            width: outputWidth,
            height: outputHeight,
            sourceWidth: display.innerWidth,
            sourceHeight: display.innerHeight,
            charset,
            render: lines.join("\n"),
            hint: `ASCII rendering of ${display.innerWidth}x${display.innerHeight} display scaled to ${outputWidth}x${outputHeight}`,
          });
        } catch (error) {
          return formatError(error as ViceError);
        }
      }
    );
  • Zod schema for renderScreen tool inputs: optional width (20-200), height (10-100), charset string.
      inputSchema: z.object({
        width: z.number().min(20).max(200).optional().describe("Output width in characters (default: 80)"),
        height: z.number().min(10).max(100).optional().describe("Output height in lines (default: 50)"),
        charset: z.string().optional().describe("Characters for shading from dark to light (default: ' .:-=+*#%@')"),
      }),
    },
Behavior3/5

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

With no annotations provided, the description carries full burden. It explains the tool creates a visual representation approximating colors and content, which is helpful behavioral context. However, it doesn't disclose important behavioral traits like whether this is a read-only operation, if it affects emulator state, performance characteristics, or error conditions.

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

Conciseness4/5

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

Well-structured with purpose first, then usage guidance, then parameter details. The description is appropriately sized with no redundant sentences. Minor deduction because the parameter section repeats schema information without adding value, slightly reducing efficiency.

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

Completeness3/5

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

For a tool with no annotations and no output schema, the description provides good purpose and usage context but lacks important behavioral details. It doesn't explain what the output looks like (ASCII art format), whether it's paginated, error conditions, or performance implications. The 100% schema coverage helps, but more behavioral context would improve completeness.

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?

Schema description coverage is 100%, so the schema already fully documents all three parameters. The description repeats the same parameter information in the 'Options' section without adding meaningful semantic context beyond what's in the schema. Baseline 3 is appropriate when schema does the heavy lifting.

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

Purpose5/5

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

The description clearly states the tool's purpose with specific verb ('Render') and resource ('current screen as ASCII art representation'). It distinguishes from sibling readScreen by explaining this creates visual representation for debugging rather than actual screen text, and mentions screenshot as another related tool.

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

Usage Guidelines5/5

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

Explicitly states when to use this tool ('for quick visual debugging without image handling') and when to use alternatives ('For actual screen text, use readScreen instead'). Also lists related tools including screenshot and readVicState, providing clear context for tool selection.

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

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/simen/vice-mcp'

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