Skip to main content
Glama
HenkDz

Self-Hosted Supabase MCP Server

create_auth_user

Create a new authentication user in Supabase by providing email and password. Use this tool to add users directly to auth.users for testing or administrative purposes.

Instructions

Creates a new user directly in auth.users. WARNING: Requires plain password, insecure. Use with extreme caution.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
emailYesThe email address for the new user.
passwordYesPlain text password (min 6 chars). WARNING: Insecure.
roleNoUser role.authenticated
user_metadataNoOptional user metadata.
app_metadataNoOptional app metadata.

Implementation Reference

  • The execute function implementing the core logic of the 'create_auth_user' tool. It performs input destructuring, checks for direct PG connection, warns about security, executes a transaction with pgcrypto check, inserts into auth.users with hashed password, parses output with Zod, and handles errors including unique email violations.
    execute: async (input: CreateAuthUserInput, context: ToolContext): Promise<CreateAuthUserOutput> => { // Use CreateAuthUserOutput
        const client = context.selfhostedClient;
        const { email, password, role, app_metadata, user_metadata } = input;
    
        // Direct DB connection is absolutely required for this direct insert
        if (!client.isPgAvailable()) {
             context.log('Direct database connection (DATABASE_URL) is required to create an auth user directly.', 'error');
            throw new Error('Direct database connection (DATABASE_URL) is required to create an auth user directly.');
        }
    
        console.warn(`SECURITY WARNING: Creating user ${email} with plain text password via direct DB insert.`);
        context.log(`Attempting to create user ${email}...`, 'warn');
    
        // Use transaction to ensure atomicity and get pg client
        const createdUser = await client.executeTransactionWithPg(async (pgClient: PoolClient) => {
            // Check if pgcrypto extension is available (needed for crypt)
            try {
                await pgClient.query("SELECT crypt('test', gen_salt('bf'))");
            } catch (err) {
                 throw new Error('Failed to execute crypt function. Ensure pgcrypto extension is enabled in the database.');
            }
            
            // Construct the INSERT statement with parameterization
            const sql = `
                INSERT INTO auth.users (
                    instance_id, email, encrypted_password, role,
                    raw_app_meta_data, raw_user_meta_data, 
                    aud, email_confirmed_at, confirmation_sent_at -- Set required defaults
                )
                VALUES (
                    COALESCE(current_setting('app.instance_id', TRUE), '00000000-0000-0000-0000-000000000000')::uuid,
                    $1, crypt($2, gen_salt('bf')),
                    $3,
                    $4::jsonb,
                    $5::jsonb,
                    'authenticated', now(), now()
                )
                RETURNING id, email, role, raw_app_meta_data, raw_user_meta_data, created_at::text, last_sign_in_at::text;
            `;
    
            const params = [
                email,
                password,
                role || 'authenticated', // Default role
                JSON.stringify(app_metadata || {}),
                JSON.stringify(user_metadata || {})
            ];
    
            try {
                const result = await pgClient.query(sql, params);
                if (result.rows.length === 0) {
                     throw new Error('User creation failed, no user returned after insert.');
                }
                return CreatedAuthUserZodSchema.parse(result.rows[0]);
            } catch (dbError: unknown) {
                let errorMessage = 'Unknown database error during user creation';
                let isUniqueViolation = false;
    
                if (typeof dbError === 'object' && dbError !== null && 'code' in dbError) {
                    // Check PG error code for unique violation safely
                    if (dbError.code === '23505') {
                        isUniqueViolation = true;
                        errorMessage = `User creation failed: Email '${email}' likely already exists.`;
                    } 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 creation: ${dbError.message}`;
                } else {
                     errorMessage = `Database error during user creation: ${String(dbError)}`;
                }
    
                console.error('Error creating user in DB:', dbError); // Log the original error
    
                // Throw a specific error message
                throw new Error(errorMessage);
            }
        });
    
        console.error(`Successfully created user ${email} with ID ${createdUser.id}.`);
        context.log(`Successfully created user ${email} with ID ${createdUser.id}.`);
        return createdUser; // Matches CreateAuthUserOutput (AuthUser)
    },
  • Zod input schema for validating the tool's input parameters including email, password, role, and metadata.
    const CreateAuthUserInputSchema = z.object({
        email: z.string().email().describe('The email address for the new user.'),
        password: z.string().min(6).describe('Plain text password (min 6 chars). WARNING: Insecure.'),
        role: z.string().optional().describe('User role.'),
        app_metadata: z.record(z.unknown()).optional().describe('Optional app metadata.'),
        user_metadata: z.record(z.unknown()).optional().describe('Optional user metadata.'),
    });
  • Zod output schema and type for the created AuthUser, matching the structure returned from the database.
    const CreatedAuthUserZodSchema = z.object({
        id: z.string().uuid(),
        email: z.string().email().nullable(),
        role: z.string().nullable(),
        created_at: z.string().nullable(),
        last_sign_in_at: z.string().nullable(), // Will likely be null on creation
        raw_app_meta_data: z.record(z.unknown()).nullable(),
        raw_user_meta_data: z.record(z.unknown()).nullable(),
        // Add other fields returned by the INSERT if necessary
    });
    // Use AuthUser for the output type hint
    type CreateAuthUserOutput = AuthUser;
  • src/index.ts:116-116 (registration)
    Registration of the createAuthUserTool in the availableTools object, making it available to the MCP server.
    [createAuthUserTool.name]: createAuthUserTool as AppTool,
  • src/index.ts:27-27 (registration)
    Import statement for the createAuthUserTool from its implementation file.
    import { createAuthUserTool } from './tools/create_auth_user.js';
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively communicates critical behavioral traits: this is a write operation (implied by 'Creates'), it requires plain text passwords (security risk), and it operates directly on the auth.users system. The warning about insecurity adds important context beyond what the input schema provides.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is perfectly sized at two sentences with zero wasted words. The first sentence states the purpose clearly, and the second provides crucial warnings. Every sentence earns its place, and the structure is front-loaded with the core functionality followed by important caveats.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a write operation with no annotations and no output schema, the description does well by clearly stating it's a creation tool and providing critical security warnings. However, it doesn't mention what happens on success/failure, whether the operation is idempotent, or what permissions are required. Given the complexity of user creation and lack of output schema, some additional behavioral context would be beneficial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the schema already documents all 5 parameters thoroughly. The description doesn't add any parameter-specific information beyond what's in the schema descriptions. However, it does reinforce the password security warning mentioned in the schema, providing marginal additional context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Creates a new user') and target resource ('directly in auth.users'), distinguishing it from sibling tools like 'update_auth_user' or 'get_auth_user'. It uses precise language that leaves no ambiguity about what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use the tool ('Creates a new user') and includes a strong warning about security implications ('WARNING: Requires plain password, insecure. Use with extreme caution.'). However, it doesn't explicitly mention when NOT to use it or name specific alternatives among the sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/HenkDz/selfhosted-supabase-mcp'

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