Skip to main content
Glama

updateProfile

Modify your Nostr social network profile by updating display name, bio, profile picture, payment addresses, and other metadata using your private key.

Instructions

Update an existing 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

  • index.ts:1275-1344 (registration)
    MCP server tool registration for 'updateProfile', including schema reference and inline handler that calls the updateProfile function and formats the response.
    server.tool( "updateProfile", "Update an existing Nostr profile (kind 0 event)", updateProfileToolConfig, async ({ privateKey, name, about, picture, nip05, lud16, lud06, website, relays }) => { try { const profileData = { name, about, picture, nip05, lud16, lud06, website }; const result = await updateProfile(privateKey, profileData, relays); if (result.success) { let response = `Profile updated 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 updated response += "\nUpdated profile 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 update profile: ${result.message}`, }, ], }; } } catch (error) { console.error("Error in updateProfile tool:", error); return { content: [ { type: "text", text: `Error updating profile: ${error instanceof Error ? error.message : "Unknown error"}`, }, ], }; } }, );
  • Zod-based input schema configuration for the updateProfile tool.
    // Schema for updateProfile tool export const updateProfileToolConfig = { 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"), };
  • Implementation of updateProfile function, which delegates profile update to createProfile since Nostr profiles are updated by publishing newer kind 0 events.
    */ export async function updateProfile( 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 }> { // For kind 0 events (profiles), updating is the same as creating // The newest event replaces the older one return createProfile(privateKey, profileData, relays); }
  • Core implementation logic for creating/publishing Nostr profile events (kind 0), called by updateProfile.
    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"}`, }; } }
  • Helper function to normalize private keys from nsec or hex format, used in profile creation.
    // Helper function to convert private key to hex if nsec format 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