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