userCRUD.ts•8.23 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 registerUserCRUDTools(server: McpServer, gql: GraphQLClient) {
// UPDATE PROFILE
const updateProfileHandler = async ({ name, avatarUrl }: { name?: string; avatarUrl?: string }) => {
try {
const mutation = `
mutation UpdateProfile($input: UpdateUserInput!) {
updateProfile(input: $input) {
id
name
avatarUrl
email
}
}
`;
const input: any = {};
if (name !== undefined) input.name = name;
if (avatarUrl !== undefined) input.avatarUrl = avatarUrl;
const data = await gql.request<{ updateProfile: any }>(mutation, { input });
return text(data.updateProfile);
} catch (error: any) {
return text({ error: error.message });
}
};
server.registerTool(
"affine_update_profile",
{
title: "Update Profile",
description: "Update current user's profile information.",
inputSchema: {
name: z.string().optional().describe("Display name"),
avatarUrl: z.string().optional().describe("Avatar URL")
}
},
updateProfileHandler as any
);
server.registerTool(
"update_profile",
{
title: "Update Profile",
description: "Update current user's profile information.",
inputSchema: {
name: z.string().optional().describe("Display name"),
avatarUrl: z.string().optional().describe("Avatar URL")
}
},
updateProfileHandler as any
);
// UPDATE SETTINGS
const updateSettingsHandler = async ({ settings }: { settings: Record<string, any> }) => {
try {
const mutation = `
mutation UpdateSettings($input: UpdateUserSettingsInput!) {
updateSettings(input: $input) {
success
}
}
`;
const data = await gql.request<{ updateSettings: any }>(mutation, {
input: settings
});
return text(data.updateSettings);
} catch (error: any) {
return text({ error: error.message });
}
};
server.registerTool(
"affine_update_settings",
{
title: "Update Settings",
description: "Update user settings and preferences.",
inputSchema: {
settings: z.record(z.any()).describe("Settings object with key-value pairs")
}
},
updateSettingsHandler as any
);
server.registerTool(
"update_settings",
{
title: "Update Settings",
description: "Update user settings and preferences.",
inputSchema: {
settings: z.record(z.any()).describe("Settings object with key-value pairs")
}
},
updateSettingsHandler as any
);
// SEND VERIFICATION EMAIL
const sendVerifyEmailHandler = async ({ callbackUrl }: { callbackUrl?: string }) => {
try {
const mutation = `
mutation SendVerifyEmail($callbackUrl: String!) {
sendVerifyEmail(callbackUrl: $callbackUrl)
}
`;
const data = await gql.request<{ sendVerifyEmail: boolean }>(mutation, {
callbackUrl: callbackUrl || `${process.env.AFFINE_BASE_URL}/verify`
});
return text({ success: data.sendVerifyEmail, message: "Verification email sent" });
} catch (error: any) {
return text({ error: error.message });
}
};
server.registerTool(
"affine_send_verify_email",
{
title: "Send Verification Email",
description: "Send email verification link.",
inputSchema: {
callbackUrl: z.string().optional().describe("Callback URL after verification")
}
},
sendVerifyEmailHandler as any
);
server.registerTool(
"send_verify_email",
{
title: "Send Verification Email",
description: "Send email verification link.",
inputSchema: {
callbackUrl: z.string().optional().describe("Callback URL after verification")
}
},
sendVerifyEmailHandler as any
);
// CHANGE PASSWORD
const changePasswordHandler = async ({ token, newPassword, userId }: { token: string; newPassword: string; userId?: string }) => {
try {
const mutation = `
mutation ChangePassword($token: String!, $newPassword: String!, $userId: String) {
changePassword(token: $token, newPassword: $newPassword, userId: $userId)
}
`;
const data = await gql.request<{ changePassword: boolean }>(mutation, {
token,
newPassword,
userId
});
return text({ success: data.changePassword, message: "Password changed successfully" });
} catch (error: any) {
return text({ error: error.message });
}
};
server.registerTool(
"affine_change_password",
{
title: "Change Password",
description: "Change user password (requires token from email).",
inputSchema: {
token: z.string().describe("Password reset token from email"),
newPassword: z.string().describe("New password"),
userId: z.string().optional().describe("User ID")
}
},
changePasswordHandler as any
);
server.registerTool(
"change_password",
{
title: "Change Password",
description: "Change user password (requires token from email).",
inputSchema: {
token: z.string().describe("Password reset token from email"),
newPassword: z.string().describe("New password"),
userId: z.string().optional().describe("User ID")
}
},
changePasswordHandler as any
);
// SEND PASSWORD RESET EMAIL
const sendPasswordResetHandler = async ({ callbackUrl }: { callbackUrl?: string }) => {
try {
const mutation = `
mutation SendChangePasswordEmail($callbackUrl: String!) {
sendChangePasswordEmail(callbackUrl: $callbackUrl)
}
`;
const data = await gql.request<{ sendChangePasswordEmail: boolean }>(mutation, {
callbackUrl: callbackUrl || `${process.env.AFFINE_BASE_URL}/reset-password`
});
return text({ success: data.sendChangePasswordEmail, message: "Password reset email sent" });
} catch (error: any) {
return text({ error: error.message });
}
};
server.registerTool(
"affine_send_password_reset",
{
title: "Send Password Reset",
description: "Send password reset email.",
inputSchema: {
callbackUrl: z.string().optional().describe("Callback URL for password reset")
}
},
sendPasswordResetHandler as any
);
server.registerTool(
"send_password_reset",
{
title: "Send Password Reset",
description: "Send password reset email.",
inputSchema: {
callbackUrl: z.string().optional().describe("Callback URL for password reset")
}
},
sendPasswordResetHandler as any
);
// DELETE ACCOUNT
const deleteAccountHandler = async ({ confirm }: { confirm: true }) => {
if (!confirm) {
return text({ error: "Confirmation required. Set confirm: true to delete account." });
}
try {
const mutation = `
mutation DeleteAccount {
deleteAccount
}
`;
const data = await gql.request<{ deleteAccount: boolean }>(mutation);
return text({ success: data.deleteAccount, message: "Account deleted successfully" });
} catch (error: any) {
return text({ error: error.message });
}
};
server.registerTool(
"affine_delete_account",
{
title: "Delete Account",
description: "Permanently delete user account. WARNING: This cannot be undone!",
inputSchema: {
confirm: z.literal(true).describe("Must be true to confirm account deletion")
}
},
deleteAccountHandler as any
);
server.registerTool(
"delete_account",
{
title: "Delete Account",
description: "Permanently delete user account. WARNING: This cannot be undone!",
inputSchema: {
confirm: z.literal(true).describe("Must be true to confirm account deletion")
}
},
deleteAccountHandler as any
);
}