Skip to main content
Glama

AFFiNE MCP Server

by DAWNCR0W
comments.ts6.31 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { GraphQLClient } from "../graphqlClient.js"; import { text } from "../util/mcp.js"; export function registerCommentTools(server: McpServer, gql: GraphQLClient, defaults: { workspaceId?: string }) { const listCommentsHandler = async (parsed: { workspaceId?: string; docId: string; first?: number; offset?: number; after?: string }) => { const workspaceId = parsed.workspaceId || defaults.workspaceId || parsed.workspaceId; if (!workspaceId) throw new Error("workspaceId required (or set AFFINE_WORKSPACE_ID)"); const query = `query ListComments($workspaceId:String!,$docId:String!,$first:Int,$offset:Int,$after:String){ workspace(id:$workspaceId){ comments(docId:$docId, pagination:{first:$first, offset:$offset, after:$after}){ totalCount pageInfo{ hasNextPage endCursor } edges{ cursor node{ id content createdAt updatedAt resolved user{ id name avatarUrl } replies{ id content createdAt updatedAt user{ id name avatarUrl } } } } } } }`; const data = await gql.request<{ workspace: any }>(query, { workspaceId, docId: parsed.docId, first: parsed.first, offset: parsed.offset, after: parsed.after }); return text(data.workspace.comments); }; server.registerTool( "affine_list_comments", { title: "List Comments", description: "List comments of a doc (with replies).", inputSchema: { workspaceId: z.string().optional(), docId: z.string(), first: z.number().optional(), offset: z.number().optional(), after: z.string().optional() } }, listCommentsHandler as any ); server.registerTool( "list_comments", { title: "List Comments", description: "List comments of a doc (with replies).", inputSchema: { workspaceId: z.string().optional(), docId: z.string(), first: z.number().optional(), offset: z.number().optional(), after: z.string().optional() } }, listCommentsHandler as any ); const createCommentHandler = async (parsed: { workspaceId?: string; docId: string; docTitle?: string; docMode?: "Page"|"Edgeless"; content: any; mentions?: string[] }) => { const workspaceId = parsed.workspaceId || defaults.workspaceId || parsed.workspaceId; if (!workspaceId) throw new Error("workspaceId required (or set AFFINE_WORKSPACE_ID)"); const mutation = `mutation CreateComment($input: CommentCreateInput!){ createComment(input:$input){ id content createdAt updatedAt resolved } }`; const input = { content: parsed.content, docId: parsed.docId, workspaceId, docTitle: parsed.docTitle || "", docMode: parsed.docMode || "Page", mentions: parsed.mentions }; const data = await gql.request<{ createComment: any }>(mutation, { input }); return text(data.createComment); }; server.registerTool( "affine_create_comment", { title: "Create Comment", description: "Create a comment on a doc.", inputSchema: { workspaceId: z.string().optional(), docId: z.string(), docTitle: z.string().optional(), docMode: z.enum(["Page","Edgeless"]).optional(), content: z.any(), mentions: z.array(z.string()).optional() } }, createCommentHandler as any ); server.registerTool( "create_comment", { title: "Create Comment", description: "Create a comment on a doc.", inputSchema: { workspaceId: z.string().optional(), docId: z.string(), docTitle: z.string().optional(), docMode: z.enum(["Page","Edgeless"]).optional(), content: z.any(), mentions: z.array(z.string()).optional() } }, createCommentHandler as any ); const updateCommentHandler = async (parsed: { id: string; content: any }) => { const mutation = `mutation UpdateComment($input: CommentUpdateInput!){ updateComment(input:$input) }`; const data = await gql.request<{ updateComment: boolean }>(mutation, { input: { id: parsed.id, content: parsed.content } }); return text({ success: data.updateComment }); }; server.registerTool( "affine_update_comment", { title: "Update Comment", description: "Update a comment content.", inputSchema: { id: z.string(), content: z.any() } }, updateCommentHandler as any ); server.registerTool( "update_comment", { title: "Update Comment", description: "Update a comment content.", inputSchema: { id: z.string(), content: z.any() } }, updateCommentHandler as any ); const deleteCommentHandler = async (parsed: { id: string }) => { const mutation = `mutation DeleteComment($id:String!){ deleteComment(id:$id) }`; const data = await gql.request<{ deleteComment: boolean }>(mutation, { id: parsed.id }); return text({ success: data.deleteComment }); }; server.registerTool( "affine_delete_comment", { title: "Delete Comment", description: "Delete a comment by id.", inputSchema: { id: z.string() } }, deleteCommentHandler as any ); server.registerTool( "delete_comment", { title: "Delete Comment", description: "Delete a comment by id.", inputSchema: { id: z.string() } }, deleteCommentHandler as any ); const resolveCommentHandler = async (parsed: { id: string; resolved: boolean }) => { const mutation = `mutation ResolveComment($input: CommentResolveInput!){ resolveComment(input:$input) }`; const data = await gql.request<{ resolveComment: boolean }>(mutation, { input: parsed }); return text({ success: data.resolveComment }); }; server.registerTool( "affine_resolve_comment", { title: "Resolve Comment", description: "Resolve or unresolve a comment.", inputSchema: { id: z.string(), resolved: z.boolean() } }, resolveCommentHandler as any ); server.registerTool( "resolve_comment", { title: "Resolve Comment", description: "Resolve or unresolve a comment.", inputSchema: { id: z.string(), resolved: z.boolean() } }, resolveCommentHandler as any ); }

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/DAWNCR0W/affine-mcp-server'

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