/** * MCP tools for rPubs (publication drafts). * * Tools: rpubs_list_drafts, rpubs_get_draft */ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import type { SyncServer } from "../local-first/sync-server"; import type { PubsDoc } from "../../modules/rpubs/schemas"; import { resolveAccess, accessDeniedResponse } from "./_auth"; const PUBS_PREFIX = ":pubs:drafts:"; function findPubsDocIds(syncServer: SyncServer, space: string): string[] { const prefix = `${space}${PUBS_PREFIX}`; return syncServer.getDocIds().filter(id => id.startsWith(prefix)); } export function registerPubsTools(server: McpServer, syncServer: SyncServer) { server.tool( "rpubs_list_drafts", "List publication drafts 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 docIds = findPubsDocIds(syncServer, space); const drafts = []; for (const docId of docIds) { const doc = syncServer.getDoc(docId); if (!doc?.draft) continue; drafts.push({ id: doc.draft.id, title: doc.draft.title, author: doc.draft.author, format: doc.draft.format, contentPreview: (doc.content || "").slice(0, 200), createdAt: doc.draft.createdAt, updatedAt: doc.draft.updatedAt, }); } return { content: [{ type: "text", text: JSON.stringify(drafts, null, 2) }] }; }, ); server.tool( "rpubs_get_draft", "Get full publication draft content", { space: z.string().describe("Space slug"), token: z.string().optional().describe("JWT auth token"), draft_id: z.string().describe("Draft ID"), }, async ({ space, token, draft_id }) => { const access = await resolveAccess(token, space, false); if (!access.allowed) return accessDeniedResponse(access.reason!); const docId = `${space}${PUBS_PREFIX}${draft_id}`; const doc = syncServer.getDoc(docId); if (!doc?.draft) { return { content: [{ type: "text", text: JSON.stringify({ error: "Draft not found" }) }] }; } return { content: [{ type: "text", text: JSON.stringify({ draft: doc.draft, content: doc.content || "", }, null, 2), }], }; }, ); }