Skip to main content
Glama

align_elements

Align multiple diagram elements horizontally or vertically to create organized layouts in Excalidraw diagrams.

Instructions

Align elements (left, center, right, top, middle, bottom)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
elementIdsYes
alignmentYes

Implementation Reference

  • The main handler function alignElementsTool that implements the align_elements tool logic. It parses input args, fetches elements, and performs alignment operations (left, center, right, top, middle, bottom) by updating element coordinates.
    export async function alignElementsTool( args: unknown, client: CanvasClient ) { const { elementIds, alignment } = AlignElementsSchema.parse(args); const elements = await Promise.all( elementIds.map(async (id) => { const el = await client.getElement(id); if (!el) throw new Error(`Element ${id} not found`); return el; }) ); switch (alignment) { case 'left': { const minX = Math.min(...elements.map((el) => el.x)); for (const el of elements) { if (el.x !== minX) { await client.updateElement(el.id, { x: minX }); } } break; } case 'right': { const maxRight = Math.max( ...elements.map((el) => el.x + (el.width ?? 0)) ); for (const el of elements) { const newX = maxRight - (el.width ?? 0); if (el.x !== newX) { await client.updateElement(el.id, { x: newX }); } } break; } case 'center': { const avgCenterX = elements.reduce((sum, el) => sum + el.x + (el.width ?? 0) / 2, 0) / elements.length; for (const el of elements) { const newX = avgCenterX - (el.width ?? 0) / 2; if (el.x !== newX) { await client.updateElement(el.id, { x: newX }); } } break; } case 'top': { const minY = Math.min(...elements.map((el) => el.y)); for (const el of elements) { if (el.y !== minY) { await client.updateElement(el.id, { y: minY }); } } break; } case 'bottom': { const maxBottom = Math.max( ...elements.map((el) => el.y + (el.height ?? 0)) ); for (const el of elements) { const newY = maxBottom - (el.height ?? 0); if (el.y !== newY) { await client.updateElement(el.id, { y: newY }); } } break; } case 'middle': { const avgCenterY = elements.reduce((sum, el) => sum + el.y + (el.height ?? 0) / 2, 0) / elements.length; for (const el of elements) { const newY = avgCenterY - (el.height ?? 0) / 2; if (el.y !== newY) { await client.updateElement(el.id, { y: newY }); } } break; } } return { success: true, aligned: true, alignment, elementIds }; }
  • Zod schema definition for AlignElements input validation, specifying elementIds array (min 2, max) and alignment enum values.
    export const AlignElementsSchema = z .object({ elementIds: z .array(z.string().max(LIMITS.MAX_ID_LENGTH)) .min(2) .max(LIMITS.MAX_ELEMENT_IDS), alignment: z.enum([ 'left', 'center', 'right', 'top', 'middle', 'bottom', ]), }) .strict();
  • Type inference for AlignElements from the schema, enabling TypeScript type checking.
    export type AlignElements = z.infer<typeof AlignElementsSchema>;
  • MCP server tool registration for align_elements with inline handler implementation, including input schema validation and the alignment logic.
    // --- Tool: align_elements --- server.tool( 'align_elements', 'Align elements (left, center, right, top, middle, bottom)', { elementIds: z.array(IdZ).min(2).max(LIMITS.MAX_ELEMENT_IDS), alignment: z.enum(['left', 'center', 'right', 'top', 'middle', 'bottom']), }, async ({ elementIds, alignment }) => { try { const elements = []; for (const eid of elementIds) { const el = await client.getElement(eid); if (!el) throw new Error(`Element ${eid} not found`); if (el.locked) throw new Error(`Element ${eid} is locked`); elements.push(el); } switch (alignment) { case 'left': { const minX = Math.min(...elements.map(e => e.x)); for (const el of elements) await client.updateElement(el.id, { x: minX }); break; } case 'right': { const maxRight = Math.max(...elements.map(e => e.x + (e.width ?? 0))); for (const el of elements) await client.updateElement(el.id, { x: maxRight - (el.width ?? 0) }); break; } case 'center': { const centers = elements.map(e => e.x + (e.width ?? 0) / 2); const avg = centers.reduce((a, b) => a + b, 0) / centers.length; for (const el of elements) await client.updateElement(el.id, { x: avg - (el.width ?? 0) / 2 }); break; } case 'top': { const minY = Math.min(...elements.map(e => e.y)); for (const el of elements) await client.updateElement(el.id, { y: minY }); break; } case 'bottom': { const maxBottom = Math.max(...elements.map(e => e.y + (e.height ?? 0))); for (const el of elements) await client.updateElement(el.id, { y: maxBottom - (el.height ?? 0) }); break; } case 'middle': { const middles = elements.map(e => e.y + (e.height ?? 0) / 2); const avgY = middles.reduce((a, b) => a + b, 0) / middles.length; for (const el of elements) await client.updateElement(el.id, { y: avgY - (el.height ?? 0) / 2 }); break; } } return { content: [{ type: 'text', text: JSON.stringify({ aligned: true, alignment, elementIds }, null, 2), }], }; } catch (err) { return { content: [{ type: 'text', text: `Error: ${(err as Error).message}` }], isError: true }; } } );
  • Export statement that makes the alignElementsTool handler available to other modules.
    export { alignElementsTool } from './align-elements.js';

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/debu-sinha/excalidraw-mcp-server'

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