Skip to main content
Glama
abushadab

Self-Hosted Supabase MCP Server

by abushadab

update_auth_user

Update user details in the auth.users table of a self-hosted Supabase instance, including email, password, role, and metadata. Requires a service_role key and direct database connection.

Instructions

Updates fields for a user in auth.users. WARNING: Password handling is insecure. Requires service_role key and direct DB connection.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
app_metadataNoNew app metadata (will overwrite existing).
emailNoNew email address.
passwordNoNew plain text password (min 6 chars). WARNING: Insecure.
roleNoNew role.
user_idYesThe UUID of the user to update.
user_metadataNoNew user metadata (will overwrite existing).

Implementation Reference

  • The execute handler function that implements the core logic for updating an auth user in the Supabase auth.users table using direct PostgreSQL queries.
        execute: async (input: UpdateAuthUserInput, context: ToolContext): Promise<UpdateAuthUserOutput> => { // Use UpdateAuthUserOutput
            const client = context.selfhostedClient;
            const { user_id, email, password, role, app_metadata, user_metadata } = input;
    
            if (!client.isPgAvailable()) {
                context.log('Direct database connection (DATABASE_URL) is required to update auth user details.', 'error');
                throw new Error('Direct database connection (DATABASE_URL) is required to update auth user details.');
            }
    
            const updates: string[] = [];
            const params: (string | object | null)[] = [];
            let paramIndex = 1;
    
            // Dynamically build SET clauses and params array
            if (email !== undefined) {
                updates.push(`email = $${paramIndex++}`);
                params.push(email);
            }
            if (password !== undefined) {
                updates.push(`encrypted_password = crypt($${paramIndex++}, gen_salt('bf'))`);
                params.push(password);
                console.warn(`SECURITY WARNING: Updating password for user ${user_id} with plain text password via direct DB update.`);
            }
            if (role !== undefined) {
                updates.push(`role = $${paramIndex++}`);
                params.push(role);
            }
            if (app_metadata !== undefined) {
                updates.push(`raw_app_meta_data = $${paramIndex++}::jsonb`);
                params.push(JSON.stringify(app_metadata));
            }
            if (user_metadata !== undefined) {
                updates.push(`raw_user_meta_data = $${paramIndex++}::jsonb`);
                params.push(JSON.stringify(user_metadata));
            }
    
            // Add user_id as the final parameter for the WHERE clause
            params.push(user_id);
            const userIdParamIndex = paramIndex;
    
            const sql = `
                UPDATE auth.users
                SET ${updates.join(', ')}, updated_at = NOW()
                WHERE id = $${userIdParamIndex}
                RETURNING id, email, role, raw_app_meta_data, raw_user_meta_data, created_at::text, updated_at::text, last_sign_in_at::text;
            `;
    
            console.error(`Attempting to update auth user ${user_id}...`);
            context.log(`Attempting to update auth user ${user_id}...`);
    
            const updatedUser = await client.executeTransactionWithPg(async (pgClient: PoolClient) => {
                 // Check pgcrypto if password is being updated
                 if (password !== undefined) {
                    try {
                        await pgClient.query("SELECT crypt('test', gen_salt('bf'))");
                    } catch (err) {
                        throw new Error('Failed to execute crypt function for password update. Ensure pgcrypto extension is enabled.');
                    }
                 }
    
                try {
                    const result = await pgClient.query(sql, params);
                    if (result.rows.length === 0) {
                        throw new Error(`User update failed: User with ID ${user_id} not found or no rows affected.`);
                    }
                    return UpdatedAuthUserZodSchema.parse(result.rows[0]);
                } catch (dbError: unknown) {
                    let errorMessage = 'Unknown database error during user update';
                    let isUniqueViolation = false;
    
                    // Check for potential email unique constraint violation if email was updated
                    if (typeof dbError === 'object' && dbError !== null && 'code' in dbError) {
                        if (email !== undefined && dbError.code === '23505') {
                            isUniqueViolation = true;
                            errorMessage = `User update failed: Email '${email}' likely already exists for another user.`;
                        } else if ('message' in dbError && typeof dbError.message === 'string') {
                            errorMessage = `Database error (${dbError.code}): ${dbError.message}`;
                        } else {
                            errorMessage = `Database error code: ${dbError.code}`;
                        }
                    } else if (dbError instanceof Error) {
                         errorMessage = `Database error during user update: ${dbError.message}`;
                    } else {
                         errorMessage = `Database error during user update: ${String(dbError)}`;
                    }
    
                    console.error('Error updating user in DB:', dbError);
                    
                    // Throw the specific error message
                    throw new Error(errorMessage);
                }
            });
    
            console.error(`Successfully updated user ${user_id}.`);
            context.log(`Successfully updated user ${user_id}.`);
            return updatedUser; // Matches UpdateAuthUserOutput (AuthUser)
        },
    }; 
  • Zod schema for input validation of the update_auth_user tool, ensuring required fields and at least one update field is provided.
    const UpdateAuthUserInputSchema = z.object({
        user_id: z.string().uuid().describe('The UUID of the user to update.'),
        email: z.string().email().optional().describe('New email address.'),
        password: z.string().min(6).optional().describe('New plain text password (min 6 chars). WARNING: Insecure.'),
        role: z.string().optional().describe('New role.'),
        app_metadata: z.record(z.unknown()).optional().describe('New app metadata (will overwrite existing).'),
        user_metadata: z.record(z.unknown()).optional().describe('New user metadata (will overwrite existing).'),
    }).refine(data => 
        data.email || data.password || data.role || data.app_metadata || data.user_metadata,
        { message: "At least one field to update (email, password, role, app_metadata, user_metadata) must be provided." }
    );
  • Zod schema for output validation of the updated auth user object.
    const UpdatedAuthUserZodSchema = z.object({
        id: z.string().uuid(),
        email: z.string().email().nullable(),
        role: z.string().nullable(),
        created_at: z.string().nullable(),
        updated_at: z.string().nullable(), // Expect this to be updated
        last_sign_in_at: z.string().nullable(),
        raw_app_meta_data: z.record(z.unknown()).nullable(),
        raw_user_meta_data: z.record(z.unknown()).nullable(),
    });
  • src/index.ts:117-117 (registration)
    Registration of the update_auth_user tool in the availableTools object, which is used to populate the MCP server's tool capabilities.
    [updateAuthUserTool.name]: updateAuthUserTool as AppTool,
  • src/index.ts:28-28 (registration)
    Import statement for the update_auth_user tool in the main index file.
    import { updateAuthUserTool } from './tools/update_auth_user.js';

Latest Blog Posts

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/abushadab/selfhosted-supabase-mcp-basic-auth'

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