/** * MCP tools for rSheet (collaborative spreadsheets). * Multi-doc: {space}:sheet:sheets:{sheetId} * * Tools: rsheet_list_sheets, rsheet_get_sheet */ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import type { SyncServer } from "../local-first/sync-server"; import { sheetDocId } from "../../modules/rsheet/schemas"; import type { SheetDoc } from "../../modules/rsheet/schemas"; import { resolveAccess, accessDeniedResponse } from "./_auth"; export function registerSheetTools(server: McpServer, syncServer: SyncServer) { server.tool( "rsheet_list_sheets", "List spreadsheets in a space", { 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 prefix = `${space}:sheet:sheets:`; const sheets: { id: string; name: string; description: string; cellCount: number; columnCount: number; createdBy: string | null; createdAt: number; updatedAt: number }[] = []; for (const docId of syncServer.listDocs()) { if (!docId.startsWith(prefix)) continue; const doc = syncServer.getDoc(docId); if (!doc?.sheet) continue; sheets.push({ id: doc.sheet.id, name: doc.sheet.name, description: doc.sheet.description, cellCount: Object.keys(doc.cells || {}).length, columnCount: Object.keys(doc.columns || {}).length, createdBy: doc.sheet.createdBy, createdAt: doc.sheet.createdAt, updatedAt: doc.sheet.updatedAt, }); } return { content: [{ type: "text" as const, text: JSON.stringify(sheets, null, 2) }] }; }, ); server.tool( "rsheet_get_sheet", "Get full sheet data (meta, columns, cells)", { space: z.string().describe("Space slug"), token: z.string().optional().describe("JWT auth token"), sheet_id: z.string().describe("Sheet ID"), }, async ({ space, token, sheet_id }) => { const access = await resolveAccess(token, space, false); if (!access.allowed) return accessDeniedResponse(access.reason!); const doc = syncServer.getDoc(sheetDocId(space, sheet_id)); if (!doc) return { content: [{ type: "text" as const, text: JSON.stringify({ error: "Sheet not found" }) }] }; return { content: [{ type: "text" as const, text: JSON.stringify({ sheet: doc.sheet, columns: doc.columns, rows: doc.rows, cells: doc.cells, }, null, 2), }], }; }, ); }