79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
/**
|
|
* 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<SheetDoc>(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<SheetDoc>(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),
|
|
}],
|
|
};
|
|
},
|
|
);
|
|
}
|