Skip to main content
Glama

createProfile

Create a new Nostr profile by generating a signed kind 0 event with display name, bio, picture, and payment addresses.

Instructions

Create a new Nostr profile (kind 0 event)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
privateKeyYesPrivate key to sign the profile with (hex format or nsec format)
nameNoDisplay name for the profile
aboutNoAbout/bio text for the profile
pictureNoURL to profile picture
nip05NoNIP-05 identifier (like email@domain.com)
lud16NoLightning address for receiving payments
lud06NoLNURL for receiving payments
websiteNoPersonal website URL
relaysNoOptional list of relays to publish to

Implementation Reference

  • The core handler function that normalizes the private key, derives public key, builds metadata event (kind 0), signs it, and publishes to relays.
    export async function createProfile(
      privateKey: string,
      profileData: {
        name?: string;
        about?: string;
        picture?: string;
        nip05?: string;
        lud16?: string;
        lud06?: string;
        website?: string;
      },
      relays: string[] = DEFAULT_RELAYS
    ): Promise<{ success: boolean, message: string, eventId?: string, publicKey?: string }> {
      try {
        // Normalize private key
        const normalizedPrivateKey = normalizePrivateKey(privateKey);
        
        // Derive public key from private key
        const publicKey = getPublicKeyFromPrivate(normalizedPrivateKey);
        
        // Create profile metadata object
        const metadata: {
          name?: string;
          about?: string;
          picture?: string;
          nip05?: string;
          lud16?: string;
          lud06?: string;
          website?: string;
        } = {};
        if (profileData.name) metadata.name = profileData.name;
        if (profileData.about) metadata.about = profileData.about;
        if (profileData.picture) metadata.picture = profileData.picture;
        if (profileData.nip05) metadata.nip05 = profileData.nip05;
        if (profileData.lud16) metadata.lud16 = profileData.lud16;
        if (profileData.lud06) metadata.lud06 = profileData.lud06;
        if (profileData.website) metadata.website = profileData.website;
        
        // Create a fresh pool for this request
        const pool = getFreshPool(relays);
        
        try {
          // Create the profile event template
          const profileTemplate = createEvent({
            kind: 0, // kind 0 is profile metadata
            content: JSON.stringify(metadata),
            tags: []
          }, publicKey);
          
          // Get event hash and sign it
          const eventId = await getEventHash(profileTemplate);
          const signature = await signEvent(eventId, normalizedPrivateKey);
          
          // Create complete signed event
          const signedProfile = {
            ...profileTemplate,
            id: eventId,
            sig: signature
          };
          
          // If no relays specified, just return success with event creation
          if (relays.length === 0) {
            return {
              success: true,
              message: 'Profile event created successfully (no relays specified for publishing)',
              eventId: signedProfile.id,
              publicKey: publicKey,
            };
          }
          
          // Publish to relays - pool.publish returns array of promises
          const pubPromises = pool.publish(relays, signedProfile);
          
          // Wait for all publish attempts to complete or timeout
          const results = await Promise.allSettled(pubPromises);
          
          // Check if at least one relay accepted the profile
          const successCount = results.filter(r => 
            r.status === 'fulfilled' && r.value?.success === true
          ).length;
          
          if (successCount === 0) {
            return {
              success: false,
              message: 'Failed to publish profile to any relay',
            };
          }
          
          return {
            success: true,
            message: `Profile published to ${successCount}/${relays.length} relays`,
            eventId: signedProfile.id,
            publicKey: publicKey,
          };
        } catch (error) {
          console.error("Error creating profile:", error);
          
          return {
            success: false,
            message: `Error creating profile: ${error instanceof Error ? error.message : "Unknown error"}`,
          };
        } finally {
          // Clean up any subscriptions and close the pool
          await pool.close();
        }
      } catch (error) {
        return {
          success: false,
          message: `Fatal error: ${error instanceof Error ? error.message : "Unknown error"}`,
        };
      }
    }
  • Zod schema defining the input parameters and validation for the createProfile tool.
    export const createProfileToolConfig = {
      privateKey: z.string().describe("Private key to sign the profile with (hex format or nsec format)"),
      name: z.string().optional().describe("Display name for the profile"),
      about: z.string().optional().describe("About/bio text for the profile"),
      picture: z.string().optional().describe("URL to profile picture"),
      nip05: z.string().optional().describe("NIP-05 identifier (like email@domain.com)"),
      lud16: z.string().optional().describe("Lightning address for receiving payments"),
      lud06: z.string().optional().describe("LNURL for receiving payments"),
      website: z.string().optional().describe("Personal website URL"),
      relays: z.array(z.string()).optional().describe("Optional list of relays to publish to"),
    };
  • index.ts:1204-1273 (registration)
    MCP tool registration using server.tool(), imports and uses createProfileToolConfig schema and createProfile handler, formats MCP response.
    server.tool(
      "createProfile",
      "Create a new Nostr profile (kind 0 event)",
      createProfileToolConfig,
      async ({ privateKey, name, about, picture, nip05, lud16, lud06, website, relays }) => {
        try {
          const profileData = {
            name,
            about,
            picture,
            nip05,
            lud16,
            lud06,
            website
          };
          
          const result = await createProfile(privateKey, profileData, relays);
          
          if (result.success) {
            let response = `Profile created successfully!\n\n`;
            response += `${result.message}\n`;
            if (result.eventId) {
              response += `Event ID: ${result.eventId}\n`;
            }
            if (result.publicKey) {
              response += `Public Key: ${formatPubkey(result.publicKey)}\n`;
            }
            
            // Show the profile data that was set
            response += "\nProfile data:\n";
            if (name) response += `Name: ${name}\n`;
            if (about) response += `About: ${about}\n`;
            if (picture) response += `Picture: ${picture}\n`;
            if (nip05) response += `NIP-05: ${nip05}\n`;
            if (lud16) response += `Lightning Address: ${lud16}\n`;
            if (lud06) response += `LNURL: ${lud06}\n`;
            if (website) response += `Website: ${website}\n`;
            
            return {
              content: [
                {
                  type: "text",
                  text: response,
                },
              ],
            };
          } else {
            return {
              content: [
                {
                  type: "text",
                  text: `Failed to create profile: ${result.message}`,
                },
              ],
            };
          }
        } catch (error) {
          console.error("Error in createProfile tool:", error);
          
          return {
            content: [
              {
                type: "text",
                text: `Error creating profile: ${error instanceof Error ? error.message : "Unknown error"}`,
              },
            ],
          };
        }
      },
    );
  • Helper to normalize private key from nsec to hex format, used in createProfile.
    function normalizePrivateKey(privateKey: string): string {
      if (privateKey.startsWith('nsec')) {
        // Validate nsec format before type assertion
        if (!/^nsec1[0-9a-z]+$/.test(privateKey)) {
          throw new Error('Invalid nsec format: must match pattern nsec1[0-9a-z]+');
        }
        
        const decoded = nip19decode(privateKey as `${string}1${string}`);
        if (decoded.type !== 'nsec') {
          throw new Error('Invalid nsec format');
        }
        return decoded.data;
      }
      
      // Validate hex format for non-nsec keys
      if (!/^[0-9a-f]{64}$/.test(privateKey)) {
        throw new Error('Invalid private key format: must be 64-character hex string or valid nsec format');
      }
      
      return privateKey;
    }

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/AustinKelsay/nostr-mcp-server'

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