Skip to main content
Glama

Nostr MCP Server

by AustinKelsay
import { jest } from '@jest/globals'; import { generateKeypair, createEvent, getEventHash, signEvent } from 'snstr'; import { NostrEvent } from '../utils/index.js'; import { processZapReceipt, validateZapReceipt, formatZapReceipt, parseZapRequestData, determineZapDirection, ZapReceipt, CachedZap } from '../zap/zap-tools.js'; describe('Zap Processing Functions', () => { let testKeys: { publicKey: string; privateKey: string }; let zapperKeys: { publicKey: string; privateKey: string }; beforeAll(async () => { testKeys = await generateKeypair(); zapperKeys = await generateKeypair(); }); describe('validateZapReceipt', () => { it('should validate a proper zap receipt', () => { const zapReceipt: NostrEvent = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', testKeys.publicKey], ['bolt11', 'lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w'], ['description', JSON.stringify({ kind: 9734, content: 'Test zap', tags: [['p', testKeys.publicKey]], pubkey: 'sender-pubkey' })] ], content: '', sig: 'test-sig' }; const result = validateZapReceipt(zapReceipt); expect(result.valid).toBe(true); expect(result.reason).toBeUndefined(); }); it('should reject invalid kind', () => { const invalidReceipt: NostrEvent = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 1, // Wrong kind tags: [], content: '', sig: 'test-sig' }; const result = validateZapReceipt(invalidReceipt); expect(result.valid).toBe(false); expect(result.reason).toContain('Not a zap receipt'); }); it('should reject missing bolt11 tag', () => { const invalidReceipt: NostrEvent = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', testKeys.publicKey], ['description', '{}'] // Missing bolt11 ], content: '', sig: 'test-sig' }; const result = validateZapReceipt(invalidReceipt); expect(result.valid).toBe(false); expect(result.reason).toContain('Missing bolt11'); }); }); describe('parseZapRequestData', () => { it('should parse zap request from description tag', () => { const zapRequest = { kind: 9734, content: 'Great post!', tags: [ ['p', testKeys.publicKey], ['amount', '100000'] ], pubkey: 'sender-pubkey', created_at: Math.floor(Date.now() / 1000) }; const zapReceipt: NostrEvent = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', testKeys.publicKey], ['bolt11', 'lnbc1pvjluez'], ['description', JSON.stringify(zapRequest)] ], content: '', sig: 'test-sig' }; const result = parseZapRequestData(zapReceipt); expect(result).toBeDefined(); expect(result?.content).toBe('Great post!'); expect(result?.pubkey).toBe('sender-pubkey'); expect(result?.amount).toBe(100000); }); it('should handle missing description tag', () => { const zapReceipt: NostrEvent = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', testKeys.publicKey], ['bolt11', 'lnbc10u1p...'] ], content: '', sig: 'test-sig' }; const result = parseZapRequestData(zapReceipt); expect(result).toBeUndefined(); }); }); describe('determineZapDirection', () => { it('should identify received zaps', () => { const zapReceipt: ZapReceipt = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', testKeys.publicKey], // Recipient ['P', 'sender-pubkey'] // Sender ], content: '', sig: 'test-sig' }; const direction = determineZapDirection(zapReceipt, testKeys.publicKey); expect(direction).toBe('received'); }); it('should identify sent zaps', () => { const zapReceipt: ZapReceipt = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', 'recipient-pubkey'], // Recipient ['P', testKeys.publicKey] // Sender ], content: '', sig: 'test-sig' }; const direction = determineZapDirection(zapReceipt, testKeys.publicKey); expect(direction).toBe('sent'); }); it('should identify self zaps', () => { const zapReceipt: ZapReceipt = { id: 'test-id', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000), kind: 9735, tags: [ ['p', testKeys.publicKey], // Recipient ['P', testKeys.publicKey] // Sender (same) ], content: '', sig: 'test-sig' }; const direction = determineZapDirection(zapReceipt, testKeys.publicKey); expect(direction).toBe('self'); }); }); describe('formatZapReceipt', () => { it('should format a zap receipt', () => { const zapRequest = { kind: 9734, content: 'Great content!', tags: [ ['p', testKeys.publicKey], ['amount', '50000'] ], pubkey: 'sender-pubkey', created_at: Math.floor(Date.now() / 1000) }; const zapReceipt: NostrEvent = { id: 'abcdef123456', pubkey: zapperKeys.publicKey, created_at: Math.floor(Date.now() / 1000) - 3600, kind: 9735, tags: [ ['p', testKeys.publicKey], ['P', 'sender-pubkey'], ['bolt11', 'lnbc1pvjluez'], ['description', JSON.stringify(zapRequest)] ], content: '', sig: 'test-sig' }; const formatted = formatZapReceipt(zapReceipt, testKeys.publicKey); // The format has changed, so we check for key elements expect(formatted).toContain('RECEIVED'); expect(formatted).toContain('Great content!'); expect(formatted).toContain('From: sender-'); }); }); });

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