createProfile
Generate and sign a Nostr profile with display name, bio, picture, payment details, and website. Publish it to specified relays using a private key.
Instructions
Create a new Nostr profile (kind 0 event)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| about | No | About/bio text for the profile | |
| lud06 | No | LNURL for receiving payments | |
| lud16 | No | Lightning address for receiving payments | |
| name | No | Display name for the profile | |
| nip05 | No | NIP-05 identifier (like email@domain.com) | |
| picture | No | URL to profile picture | |
| privateKey | Yes | Private key to sign the profile with (hex format or nsec format) | |
| relays | No | Optional list of relays to publish to | |
| website | No | Personal website URL |
Implementation Reference
- profile/profile-tools.ts:107-218 (handler)Core implementation of the createProfile tool: normalizes private key, derives public key, builds metadata event (kind 0), signs it, and publishes to specified 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"}`, }; } }
- profile/profile-tools.ts:14-25 (schema)Zod schema defining input parameters for the createProfile tool.// Schema for 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 server.tool registration for 'createProfile', including input validation via schema and thin wrapper handler that delegates to core createProfile function and formats 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"}`, }, ], }; } }, );
- profile/profile-tools.ts:49-69 (helper)Helper function 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; }