List Workspaces
list_workspacesRetrieve all available workspaces on the AFFiNE MCP Server to manage documents, search content, handle comments, and access version history efficiently.
Instructions
List all available AFFiNE workspaces
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools/workspaces.ts:136-144 (handler)The core handler function that implements the list_workspaces tool logic by querying the GraphQL endpoint for available workspaces and returning the list or an error.
const listWorkspacesHandler = async () => { try { const query = `query { workspaces { id public enableAi createdAt } }`; const data = await gql.request<{ workspaces: any[] }>(query); return text(data.workspaces || []); } catch (error: any) { return text({ error: error.message }); } }; - src/tools/workspaces.ts:146-153 (registration)Direct registration of the 'list_workspaces' tool with the MCP server, including metadata and handler reference.
server.registerTool( "list_workspaces", { title: "List Workspaces", description: "List all available AFFiNE workspaces" }, listWorkspacesHandler as any ); - src/tools/workspaces.ts:154-161 (registration)Additional registration of an alias 'affine_list_workspaces' using the same handler.
server.registerTool( "affine_list_workspaces", { title: "List Workspaces", description: "List all available AFFiNE workspaces" }, listWorkspacesHandler ); - src/index.ts:65-65 (registration)Top-level call to register all workspace-related tools, including list_workspaces, during server initialization.
registerWorkspaceTools(server, gql); - src/tools/workspaces.ts:134-468 (helper)Helper function that defines and registers all workspace tools, including the listWorkspacesHandler and its registrations.
export function registerWorkspaceTools(server: McpServer, gql: GraphQLClient) { // LIST WORKSPACES const listWorkspacesHandler = async () => { try { const query = `query { workspaces { id public enableAi createdAt } }`; const data = await gql.request<{ workspaces: any[] }>(query); return text(data.workspaces || []); } catch (error: any) { return text({ error: error.message }); } }; server.registerTool( "list_workspaces", { title: "List Workspaces", description: "List all available AFFiNE workspaces" }, listWorkspacesHandler as any ); server.registerTool( "affine_list_workspaces", { title: "List Workspaces", description: "List all available AFFiNE workspaces" }, listWorkspacesHandler ); // GET WORKSPACE const getWorkspaceHandler = async ({ id }: { id: string }) => { try { const query = `query GetWorkspace($id: String!) { workspace(id: $id) { id public enableAi createdAt permissions { Workspace_Read Workspace_CreateDoc } } }`; const data = await gql.request<{ workspace: any }>(query, { id }); return text(data.workspace); } catch (error: any) { return text({ error: error.message }); } }; server.registerTool( "get_workspace", { title: "Get Workspace", description: "Get details of a specific workspace", inputSchema: { id: z.string().describe("Workspace ID") } }, getWorkspaceHandler as any ); server.registerTool( "affine_get_workspace", { title: "Get Workspace", description: "Get details of a specific workspace", inputSchema: { id: z.string().describe("Workspace ID") } }, getWorkspaceHandler as any ); // CREATE WORKSPACE const createWorkspaceHandler = async ({ name, avatar }: { name: string; avatar?: string }) => { try { // Get endpoint and headers from GraphQL client const endpoint = (gql as any).endpoint || process.env.AFFINE_BASE_URL + '/graphql'; const headers = (gql as any).headers || {}; const cookie = (gql as any).cookie || headers.Cookie || ''; // Create initial workspace data const { workspaceUpdate, firstDocId, docUpdate } = createInitialWorkspaceData(name); // Only send workspace update - document will be created separately const initData = Buffer.from(workspaceUpdate); // Create multipart form const form = new FormData(); // Add GraphQL operation form.append('operations', JSON.stringify({ name: 'createWorkspace', query: `mutation createWorkspace($init: Upload!) { createWorkspace(init: $init) { id public createdAt enableAi } }`, variables: { init: null } })); // Map file to variable form.append('map', JSON.stringify({ '0': ['variables.init'] })); // Add workspace init data form.append('0', initData, { filename: 'init.yjs', contentType: 'application/octet-stream' }); // Send request const response = await fetch(endpoint, { method: 'POST', headers: { ...headers, 'Cookie': cookie, ...form.getHeaders() }, body: form as any }); const result = await response.json() as any; if (result.errors) { throw new Error(result.errors[0].message); } const workspace = result.data.createWorkspace; // Now create the actual document via WebSocket const wsUrl = endpoint.replace('https://', 'wss://').replace('http://', 'ws://').replace('/graphql', ''); return new Promise((resolve) => { const socket = io(wsUrl, { transports: ['websocket'], path: '/socket.io/', extraHeaders: cookie ? { Cookie: cookie } : undefined }); socket.on('connect', () => { // Join the workspace socket.emit('space:join', { spaceType: 'workspace', spaceId: workspace.id }); // Send the document update setTimeout(() => { const docUpdateBase64 = Buffer.from(docUpdate).toString('base64'); socket.emit('space:push-doc-update', { spaceType: 'workspace', spaceId: workspace.id, docId: firstDocId, update: docUpdateBase64 }); // Wait longer for sync and disconnect setTimeout(() => { socket.disconnect(); resolve(text({ ...workspace, name: name, avatar: avatar, firstDocId: firstDocId, status: "success", message: "Workspace created successfully", url: `${process.env.AFFINE_BASE_URL}/workspace/${workspace.id}` })); }, 3000); }, 1000); }); socket.on('error', () => { socket.disconnect(); // Even if WebSocket fails, workspace was created resolve(text({ ...workspace, name: name, avatar: avatar, firstDocId: firstDocId, status: "partial", message: "Workspace created (document sync may be pending)", url: `${process.env.AFFINE_BASE_URL}/workspace/${workspace.id}` })); }); // Timeout setTimeout(() => { socket.disconnect(); resolve(text({ ...workspace, name: name, avatar: avatar, firstDocId: firstDocId, status: "success", message: "Workspace created", url: `${process.env.AFFINE_BASE_URL}/workspace/${workspace.id}` })); }, 10000); }); } catch (error: any) { return text({ error: error.message, status: "failed" }); } }; server.registerTool( "create_workspace", { title: "Create Workspace", description: "Create a new workspace with initial document (accessible in UI)", inputSchema: { name: z.string().describe("Workspace name"), avatar: z.string().optional().describe("Avatar emoji or URL") } }, createWorkspaceHandler as any ); server.registerTool( "affine_create_workspace", { title: "Create Workspace", description: "Create a new workspace with initial document (accessible in UI)", inputSchema: { name: z.string().describe("Workspace name"), avatar: z.string().optional().describe("Avatar emoji or URL") } }, createWorkspaceHandler as any ); server.registerTool( "affine_create_workspace_fixed", { title: "Create Workspace (Fixed)", description: "Create a new workspace with initial document (backward compatible alias)", inputSchema: { name: z.string().describe("Workspace name"), avatar: z.string().optional().describe("Avatar emoji or URL") } }, createWorkspaceHandler as any ); // UPDATE WORKSPACE const updateWorkspaceHandler = async ({ id, public: isPublic, enableAi }: { id: string; public?: boolean; enableAi?: boolean }) => { try { const mutation = ` mutation UpdateWorkspace($input: UpdateWorkspaceInput!) { updateWorkspace(input: $input) { id public } } `; const input: any = { id }; if (isPublic !== undefined) input.public = isPublic; const data = await gql.request<{ updateWorkspace: any }>(mutation, { input }); return text(data.updateWorkspace); } catch (error: any) { return text({ error: error.message }); } }; server.registerTool( "update_workspace", { title: "Update Workspace", description: "Update workspace settings", inputSchema: { id: z.string().describe("Workspace ID"), public: z.boolean().optional().describe("Make workspace public"), enableAi: z.boolean().optional().describe("Enable AI features") } }, updateWorkspaceHandler as any ); server.registerTool( "affine_update_workspace", { title: "Update Workspace", description: "Update workspace settings", inputSchema: { id: z.string().describe("Workspace ID"), public: z.boolean().optional().describe("Make workspace public"), enableAi: z.boolean().optional().describe("Enable AI features") } }, updateWorkspaceHandler as any ); // DELETE WORKSPACE const deleteWorkspaceHandler = async ({ id }: { id: string }) => { try { const mutation = ` mutation DeleteWorkspace($id: String!) { deleteWorkspace(id: $id) } `; const data = await gql.request<{ deleteWorkspace: boolean }>(mutation, { id }); return text({ success: data.deleteWorkspace, message: "Workspace deleted successfully" }); } catch (error: any) { return text({ error: error.message }); } }; server.registerTool( "delete_workspace", { title: "Delete Workspace", description: "Delete a workspace permanently", inputSchema: { id: z.string().describe("Workspace ID") } }, deleteWorkspaceHandler as any ); server.registerTool( "affine_delete_workspace", { title: "Delete Workspace", description: "Delete a workspace permanently", inputSchema: { id: z.string().describe("Workspace ID") } }, deleteWorkspaceHandler as any ); }