Engineering Journal
Schema Editor
Schema Editor

Exposing a Canvas Tool to an AI Agent via MCP: The Serialization Contract Comes First

2026-06-04

TLDR

Wiring Model Context Protocol (MCP) servers into canvas tools so AI agents can inspect and modify diagrams is mostly a serialization challenge, not a protocol issue. While protocol setup takes five lines of glue code, returning raw SVG strings forces LLMs to reverse-engineer semantic meaning from visual paint orders. Establishing a versioned JSON serialization contract (schema: 'ginexys-diagram-v2') separating component metadata and explicit port connection records provides a stable surface for AI reasoning.
Integration TierReturned Data FormatAI Reasoning QualityContract Maintenance
Raw SVG DumpRaw innerHTML stringUnreliable (Misses components, guesses links)Zero protocol overhead
Versioned JSON SchemaStructured domain payloadExact (Explicit types & connection pairs)Requires schema builder methods

Problem statement: the flaws of SVG protocol dumps

When connecting diagram tools to AI agents using MCP, developers often take an easy shortcut: returning raw SVG strings directly in response to read_diagram tool calls:

// NAIVE IMPLEMENTATION: Returning raw SVG string
function handleMcpReadDiagram() {
  return { content: [{ type: 'text', text: document.getElementById('canvas-svg').innerHTML }] };
}

This returns internal DOM IDs, CSS style declarations, and SVG path coordinates. The AI receives a noisy document where semantic component relationships are buried under rendering implementation details.


Technical failure mode: inferring topology from visual layout

LLMs parsing raw SVG face three severe issues:

  1. Uncertain Component Types: A resistor symbol rendered as nested SVG <path> elements within a <g> container requires the LLM to guess component classification based on unstandardized CSS class names.
  2. Ambiguous Wire Topology: SVG paths contain visual curve segments (M... C...), but no explicit connection records. The LLM must infer electrical connections from coordinate proximity, leading to incorrect netlist analysis.

The fix & architecture: versioned domain serialization contracts

Define a versioned JSON schema representing domain topology directly:

// Ground-Truth Serialization Contract (ginexys-diagram-v2)
function buildDiagramPayload(editor) {
  return {
    schema: 'ginexys-diagram-v2',
    domain: 'electrical',
    components: editor.getComponents().map(c => ({
      id: c.id,
      type: c.symbolType,       // 'resistor', 'capacitor', 'op-amp'
      label: c.label,
      position: { x: c.bbox.cx, y: c.bbox.cy },
      properties: c.properties, // { value: '10k', tolerance: '5%' }
      ports: c.ports.map(p => ({ name: p.name, position: p.worldPos }))
    })),
    connections: editor.getWires().map(w => ({
      id: w.id,
      from: { componentId: w.startComponentId, portName: w.startPortName },
      to: { componentId: w.endComponentId, portName: w.endPortName },
      net: w.netName
    })),
    metadata: { exportedAt: new Date().toISOString(), componentCount: editor.getComponents().length }
  };
}

Protocol handler (extension host)

The MCP server handler returns the structured JSON payload cleanly:
// MCP Server Request Handler
server.setRequestHandler(CallToolRequestSchema, async (req) => {
  if (req.params.name === 'read_diagram') {
    const payload = await webviewPanel.webview.postMessage({ type: 'mcp:read-schema' });
    return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
  }
});
Rule of thumb: Always version your MCP serialization payload (schema: 'v2') and return structured domain entities rather than visual rendering markup like SVG or HTML.
Read this post in the full Engineering Journal →