/** * MCP tools for rDesign (document design / page layout). * * Tools: rdesign_get_document, rdesign_list_frames */ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import type { SyncServer } from "../local-first/sync-server"; import { designDocId } from "../../modules/rdesign/schemas"; import type { DesignDoc } from "../../modules/rdesign/schemas"; import { resolveAccess, accessDeniedResponse } from "./_auth"; export function registerDesignTools(server: McpServer, syncServer: SyncServer) { server.tool( "rdesign_get_document", "Get design document overview with pages", { space: z.string().describe("Space slug"), token: z.string().optional().describe("JWT auth token"), }, async ({ space, token }) => { const access = await resolveAccess(token, space, false); if (!access.allowed) return accessDeniedResponse(access.reason!); const doc = syncServer.getDoc(designDocId(space)); if (!doc?.document) return { content: [{ type: "text", text: JSON.stringify({ error: "No design document found" }) }] }; const pages = Object.values(doc.document.pages || {}); const frameCount = Object.keys(doc.document.frames || {}).length; return { content: [{ type: "text", text: JSON.stringify({ title: doc.document.title, unit: doc.document.unit, pageCount: pages.length, pages, frameCount, }, null, 2), }], }; }, ); server.tool( "rdesign_list_frames", "List design frames on a specific page", { space: z.string().describe("Space slug"), token: z.string().optional().describe("JWT auth token"), page_number: z.number().optional().describe("Filter by page number"), type: z.string().optional().describe("Filter by frame type (text, image, rect, ellipse)"), }, async ({ space, token, page_number, type }) => { const access = await resolveAccess(token, space, false); if (!access.allowed) return accessDeniedResponse(access.reason!); const doc = syncServer.getDoc(designDocId(space)); if (!doc?.document) return { content: [{ type: "text", text: JSON.stringify({ error: "No design document found" }) }] }; let frames = Object.values(doc.document.frames || {}); if (page_number !== undefined) frames = frames.filter(f => f.page === page_number); if (type) frames = frames.filter(f => f.type === type); return { content: [{ type: "text", text: JSON.stringify(frames, null, 2) }] }; }, ); }