Skip to main content
Glama

DeSo MCP Server

Official
deso-mcp.js155 kB
#!/usr/bin/env node /** * DeSo MCP Server v3.0 - Comprehensive DeSo Development Assistant * * Enhanced with real debugging experience and implementation patterns from building * a complete DeSo messaging application. Includes solutions to all major pitfalls * and best practices learned through extensive debugging sessions. * * NEW IN v3.0: * 🛠️ deso_debugging_guide - Comprehensive debugging for common DeSo issues * 🏗️ deso_implementation_patterns - Best practices from deso-chat analysis * * CORE DEBUGGING FIXES INCLUDED: * ✅ Message Decryption: DecryptedMessage vs MessageText property fix * ✅ Access Groups: Empty string handling vs undefined checks * ✅ Infinite Loops: useCallback dependency management * ✅ API Responses: Proper response structure handling per endpoint * ✅ Authentication: Complete identity event handling and user switching * ✅ React Integration: Key warnings, state management, error boundaries * * IMPLEMENTATION PATTERNS: * 📱 Complete messaging flow with encryption and access groups * 🛡️ Comprehensive error handling with retry logic * 🔄 Professional state management patterns * 🎯 Real-time polling and optimistic updates * * Based on real debugging experience building a production DeSo messaging app * with reference to deso-chat implementation patterns. */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import fs from 'fs/promises'; import path from 'path'; import http from 'http'; // Get port from environment variable or default to 3000 const PORT = process.env.PORT || 3000; const HOST = process.env.HOST || '0.0.0.0'; // Bind to all interfaces for Docker compatibility // Create server with explicit error handling const server = new Server( { name: "deso-mcp-final", version: "3.0.0", // Updated with comprehensive debugging tools }, { capabilities: { tools: {}, }, } ); // Add error handling server.onerror = (error) => { console.error("DeSo MCP Server error:", error); }; // List tools handler with explicit error handling server.setRequestHandler(ListToolsRequestSchema, async () => { try { const tools = [ { name: "deso_api_explorer", description: "Comprehensive DeSo API explorer with backend implementation details and deso-js SDK integration", inputSchema: { type: "object", properties: { category: { type: "string", enum: ["social", "financial", "nft", "dao", "tokens", "access", "associations", "derived-keys", "messages", "data", "notifications", "media", "admin", "blockchain", "identity", "all"], description: "API category to explore" }, endpoint: { type: "string", description: "Specific endpoint name (optional)" }, includeCode: { type: "boolean", description: "Include code examples" } } } }, { name: "deso_js_guide", description: "Complete guide to using the deso-js SDK with setup, authentication, and transactions", inputSchema: { type: "object", properties: { topic: { type: "string", enum: ["setup", "identity", "authentication", "transactions", "data", "permissions", "examples", "troubleshooting"], description: "Topic to get guidance on" }, framework: { type: "string", enum: ["vanilla", "react", "nextjs", "node"], description: "Framework context (optional)" } }, required: ["topic"] } }, { name: "generate_deso_code", description: "Generate comprehensive code examples for DeSo operations using deso-js SDK", inputSchema: { type: "object", properties: { operation: { type: "string", description: "DeSo operation (e.g., 'follow', 'post', 'buy-creator-coin', 'send-diamonds')" }, language: { type: "string", enum: ["javascript", "typescript", "react", "curl"], description: "Programming language/framework" }, includeAuth: { type: "boolean", description: "Include authentication setup" }, fullExample: { type: "boolean", description: "Generate complete working example" } }, required: ["operation", "language"] } }, { name: "explain_deso_architecture", description: "Explain DeSo architecture, flows, and integration patterns", inputSchema: { type: "object", properties: { topic: { type: "string", description: "Architecture topic to explain" }, includeCode: { type: "boolean", description: "Include code examples" } }, required: ["topic"] } }, { name: "repository_search", description: "Search for documents in the DeSo repository", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query" } }, required: ["query"] } }, { name: "read_repository_document", description: "Read a specific document from the DeSo repository", inputSchema: { type: "object", properties: { path: { type: "string", description: "Relative path to the document (e.g., 'docs/deso-tutorial-build-apps.md')" }, repository: { type: "string", enum: ["docs", "core", "identity", "frontend", "backend", "deso-js", "deso-chat", "deso-ui", "graphql"], description: "Repository name" } }, required: ["path"] } }, { name: "deso_debugging_guide", description: "Comprehensive debugging guide for common DeSo integration issues with solutions", inputSchema: { type: "object", properties: { issue: { type: "string", enum: ["message-decryption", "access-groups", "infinite-loops", "api-responses", "authentication", "react-errors", "all"], description: "Specific issue to debug or 'all' for complete guide" }, includeCode: { type: "boolean", description: "Include code examples and fixes" } }, required: ["issue"] } }, { name: "deso_implementation_patterns", description: "Best practices and implementation patterns learned from deso-chat and real debugging", inputSchema: { type: "object", properties: { pattern: { type: "string", enum: ["messaging-flow", "error-handling", "state-management", "api-integration", "user-switching", "real-time-updates", "all"], description: "Implementation pattern to explore" }, framework: { type: "string", enum: ["react", "vanilla", "nextjs"], description: "Framework context" } }, required: ["pattern"] } }, { name: "deso_ui_components", description: "Comprehensive DeSo UI component library explorer with installation commands, usage examples, and component relationships", inputSchema: { type: "object", properties: { action: { type: "string", enum: ["explore", "install", "usage", "dependencies", "examples", "layouts", "categories", "search"], description: "Action to perform with the UI component library" }, component: { type: "string", description: "Specific component name (e.g., 'post-card', 'editor', 'profile-picture')" }, category: { type: "string", enum: ["social", "user", "messaging", "media", "interactive", "navigation", "layout", "all"], description: "Component category to explore" }, framework: { type: "string", enum: ["react", "nextjs", "vanilla"], description: "Target framework for examples" }, query: { type: "string", description: "Search query for components" } }, required: ["action"] } }, { name: "deso_graphql_helper", description: "GraphQL query builder and schema explorer for DeSo blockchain data. Helps write GraphQL queries to get user data, posts, followers, and more from the DeSo GraphQL API.", inputSchema: { type: "object", properties: { action: { type: "string", enum: ["query", "schema", "examples", "build", "explain"], description: "Action to perform with GraphQL" }, queryType: { type: "string", enum: ["user", "posts", "followers", "following", "likes", "diamonds", "messages", "nfts", "custom"], description: "Type of query to build or explain" }, username: { type: "string", description: "Username to query for (e.g., 'nader')" }, publicKey: { type: "string", description: "Public key to query for" }, question: { type: "string", description: "Natural language question to convert to GraphQL (e.g., 'how many followers does nader have?')" }, customQuery: { type: "string", description: "Custom GraphQL query to explain or validate" } }, required: ["action"] } } ]; console.error("DeSo MCP: Returning", tools.length, "tools"); return { tools }; } catch (error) { console.error("Error in DeSo ListTools:", error); throw error; } }); // Call tool handler with your comprehensive implementations server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; console.error("DeSo MCP: Tool called:", name); switch (name) { case "deso_api_explorer": return await exploreDesoApi(args); case "deso_js_guide": return await getDesoJsGuide(args); case "generate_deso_code": return await generateDesoCode(args); case "explain_deso_architecture": return await explainDesoArchitecture(args); case "repository_search": return await repositorySearch(args); case "read_repository_document": return await readRepositoryDocument(args); case "deso_debugging_guide": return await desoDebuggingGuide(args); case "deso_implementation_patterns": return await desoImplementationPatterns(args); case "deso_ui_components": return await desoUIComponents(args); case "deso_graphql_helper": return await desoGraphQLHelper(args); default: throw new Error(`Unknown DeSo tool: ${name}`); } } catch (error) { console.error("Error in DeSo CallTool:", error); throw error; } }); // COMPREHENSIVE DeSo API Implementation (from original mcp-server.js) async function exploreDesoApi(args) { const { category = 'all', endpoint, includeCode = false } = args; // Complete API Details from original comprehensive server (FULLY RESTORED) const apiDetails = { social: { description: "Social interaction endpoints for posts, follows, likes, diamonds, and messaging", backendFile: "routes/transaction.go, routes/post.go, routes/new_message.go", documentation: { tutorials: ["docs/deso-tutorial-build-apps.md"], architecture: ["docs/architecture-overview/"], examples: ["docs/deso-applications.md"] }, endpoints: { "create-follow-txn-stateless": { method: "POST", url: "/api/v0/create-follow-txn-stateless", handler: "CreateFollowTxnStateless", description: "Creates unsigned follow/unfollow transaction", desoJs: "updateFollowingStatus", params: { required: ["FollowerPublicKeyBase58Check", "FollowedPublicKeyBase58Check", "IsUnfollow", "MinFeeRateNanosPerKB"], optional: ["TransactionFees"] } }, "submit-post": { method: "POST", url: "/api/v0/submit-post", handler: "SubmitPost", description: "Submit new post or modify existing post", desoJs: "submitPost", params: { required: ["UpdaterPublicKeyBase58Check", "BodyObj", "MinFeeRateNanosPerKB"], optional: ["PostHashHexToModify", "ParentStakeID", "RepostedPostHashHex", "PostExtraData", "IsHidden"] } }, "create-like-stateless": { method: "POST", url: "/api/v0/create-like-stateless", handler: "CreateLikeStateless", description: "Creates unsigned like/unlike transaction", desoJs: "updateLikeStatus", params: { required: ["ReaderPublicKeyBase58Check", "LikedPostHashHex", "IsUnlike", "MinFeeRateNanosPerKB"], optional: ["TransactionFees"] } }, "update-profile": { method: "POST", url: "/api/v0/update-profile", handler: "UpdateProfile", description: "Update user profile information", desoJs: "updateProfile", params: { required: ["UpdaterPublicKeyBase58Check", "NewCreatorBasisPoints", "NewStakeMultipleBasisPoints", "MinFeeRateNanosPerKB"], optional: ["ProfilePublicKeyBase58Check", "NewUsername", "NewDescription", "NewProfilePic", "IsHidden", "ExtraData"] } }, "send-diamonds": { method: "POST", url: "/api/v0/send-diamonds", handler: "SendDiamonds", description: "Send diamond tips to posts", desoJs: "sendDiamonds", params: { required: ["SenderPublicKeyBase58Check", "ReceiverPublicKeyBase58Check", "DiamondPostHashHex", "DiamondLevel", "MinFeeRateNanosPerKB"], optional: ["ExtraData", "TransactionFees"] } } } }, messages: { description: "Modern DeSo messaging system with DM and group chat support", backendFile: "routes/new_message.go", documentation: { endpoints: ["docs/deso-backend/api/messages-endpoints.md"], transactions: ["docs/deso-backend/construct-transactions/social-transactions-api.md"] }, endpoints: { "send-dm-message": { method: "POST", url: "/api/v0/send-dm-message", handler: "SendDmMessage", description: "Send private message between users (replaces deprecated send-message-stateless)", desoJs: "sendDMMessage", params: { required: [ "SenderAccessGroupOwnerPublicKeyBase58Check", "SenderAccessGroupPublicKeyBase58Check", "SenderAccessGroupKeyName", "RecipientAccessGroupOwnerPublicKeyBase58Check", "RecipientAccessGroupPublicKeyBase58Check", "RecipientAccessGroupKeyName", "EncryptedMessageText", "MinFeeRateNanosPerKB" ], optional: ["TransactionFees", "ExtraData"] } }, "update-dm-message": { method: "POST", url: "/api/v0/update-dm-message", handler: "UpdateDmMessage", description: "Update existing direct message", desoJs: "updateDMMessage", params: { required: [ "SenderAccessGroupOwnerPublicKey", "SenderAccessGroupPublicKey", "SenderAccessGroupKeyName", "RecipientAccessGroupOwnerPublicKey", "RecipientAccessGroupPublicKey", "RecipientAccessGroupKeyName", "EncryptedMessageText", "TimestampNanosString" ], optional: ["MinFeeRateNanosPerKB", "TransactionFees", "ExtraData"] } }, "get-user-dm-threads-ordered-by-timestamp": { method: "POST", url: "/api/v0/get-user-dm-threads-ordered-by-timestamp", handler: "GetUserDmThreadsOrderedByTimestamp", description: "Get user's DM conversation list ordered by most recent message", desoJs: "getAllMessageThreads", params: { required: ["UserPublicKeyBase58Check"], optional: [] } }, "get-paginated-messages-for-dm-thread": { method: "POST", url: "/api/v0/get-paginated-messages-for-dm-thread", handler: "GetPaginatedMessagesForDmThread", description: "Get messages for specific DM conversation with pagination", desoJs: "getPaginatedDMThread", params: { required: [ "UserGroupOwnerPublicKeyBase58Check", "UserGroupKeyName", "PartyGroupOwnerPublicKeyBase58Check", "PartyGroupKeyName", "MaxMessagesToFetch" ], optional: ["StartTimestampString", "StartTimestamp"] } }, "send-group-chat-message": { method: "POST", url: "/api/v0/send-group-chat-message", handler: "SendGroupChatMessage", description: "Send message to group chat", desoJs: "sendGroupChatMessage", params: { required: [ "SenderAccessGroupOwnerPublicKey", "SenderAccessGroupPublicKeyBase58Check", "SenderAccessGroupKeyName", "RecipientAccessGroupOwnerPublicKeyBase58Check", "RecipientAccessGroupPublicKeyBase58Check", "RecipientAccessGroupKeyName", "EncryptedMessageText" ], optional: ["MinFeeRateNanosPerKB", "TransactionFees", "ExtraData"] } }, "update-group-chat-message": { method: "POST", url: "/api/v0/update-group-chat-message", handler: "UpdateGroupChatMessage", description: "Update existing group chat message", desoJs: "updateGroupChatMessage", params: { required: [ "SenderAccessGroupOwnerPublicKey", "SenderAccessGroupPublicKey", "SenderAccessGroupKeyName", "RecipientAccessGroupOwnerPublicKey", "RecipientAccessGroupPublicKey", "RecipientAccessGroupKeyName", "EncryptedMessageText", "TimestampNanosString" ], optional: ["MinFeeRateNanosPerKB", "TransactionFees", "ExtraData"] } }, "get-user-group-chat-threads-ordered-by-timestamp": { method: "POST", url: "/api/v0/get-user-group-chat-threads-ordered-by-timestamp", handler: "GetUserGroupChatThreadsOrderedByTimestamp", description: "Get user's group chat list ordered by most recent message", desoJs: "getAllMessageThreads", params: { required: ["UserPublicKeyBase58Check"], optional: [] } }, "get-paginated-messages-for-group-chat-thread": { method: "POST", url: "/api/v0/get-paginated-messages-for-group-chat-thread", handler: "GetPaginatedMessagesForGroupChatThread", description: "Get messages for specific group chat with pagination", desoJs: "getPaginatedGroupChatThread", params: { required: [ "UserPublicKeyBase58Check", "AccessGroupKeyName", "MaxMessagesToFetch" ], optional: ["StartTimestampString", "StartTimestamp"] } }, "get-all-user-message-threads": { method: "POST", url: "/api/v0/get-all-user-message-threads", handler: "GetAllUserMessageThreads", description: "Get all user conversations (DMs + group chats) ordered by timestamp", desoJs: "getAllMessageThreads", params: { required: ["UserPublicKeyBase58Check"], optional: [] } }, "send-message-stateless": { method: "POST", url: "/api/v0/send-message-stateless", handler: "SendMessageStateless", description: "⚠️ DEPRECATED - Use send-dm-message instead. Legacy message sending endpoint", desoJs: "sendDMMessage (maps to new endpoint)", params: { required: ["SenderPublicKeyBase58Check", "RecipientPublicKeyBase58Check", "MessageText", "MinFeeRateNanosPerKB"], optional: ["EncryptedMessageText", "SenderMessagingPublicKey", "SenderMessagingKeyName", "RecipientMessagingPublicKey", "RecipientMessagingKeyName", "ExtraData"], deprecated: true } } } }, financial: { description: "Financial transactions including creator coins, DeSo transfers, and diamonds", backendFile: "routes/transaction.go", endpoints: { "buy-or-sell-creator-coin": { method: "POST", url: "/api/v0/buy-or-sell-creator-coin", handler: "BuyOrSellCreatorCoin", description: "Buy or sell creator coins", desoJs: "buyCreatorCoin / sellCreatorCoin", params: { required: ["UpdaterPublicKeyBase58Check", "CreatorPublicKeyBase58Check", "OperationType", "MinFeeRateNanosPerKB"], optional: ["DeSoToSellNanos", "CreatorCoinToSellNanos", "MinDeSoExpectedNanos", "MinCreatorCoinExpectedNanos"] } }, "send-deso": { method: "POST", url: "/api/v0/send-deso", handler: "SendDeSo", description: "Send DeSo to another user", desoJs: "sendDeso", params: { required: ["SenderPublicKeyBase58Check", "RecipientPublicKeyOrUsername", "AmountNanos", "MinFeeRateNanosPerKB"], optional: ["ExtraData", "TransactionFees"] } }, "transfer-creator-coin": { method: "POST", url: "/api/v0/transfer-creator-coin", handler: "TransferCreatorCoin", description: "Transfer creator coins between users", desoJs: "transferCreatorCoin", params: { required: ["SenderPublicKeyBase58Check", "CreatorPublicKeyBase58Check", "ReceiverUsernameOrPublicKeyBase58Check", "CreatorCoinToTransferNanos", "MinFeeRateNanosPerKB"], optional: ["TransactionFees"] } }, "exchange-bitcoin-stateless": { method: "POST", url: "/api/v0/exchange-bitcoin-stateless", handler: "ExchangeBitcoinStateless", description: "Exchange Bitcoin for DeSo", desoJs: "exchangeBitcoin", params: { required: ["PublicKeyBase58Check", "BurnAmountSatoshis", "FeeRateSatoshisPerKB", "LatestBitcionAPIResponse", "BTCDepositAddress"], optional: ["DerivedPublicKeyBase58Check", "Broadcast", "SignedHashes"] } } } }, nft: { description: "NFT transaction endpoints for minting, bidding, transferring, and burning NFTs", backendFile: "routes/nft.go", endpoints: { "create-nft": { method: "POST", url: "/api/v0/create-nft", handler: "CreateNFT", description: "Create/mint a new NFT from a post", desoJs: "createNft", params: { required: ["UpdaterPublicKeyBase58Check", "NFTPostHashHex", "NumCopies", "MinFeeRateNanosPerKB"], optional: ["NFTRoyaltyToCreatorBasisPoints", "NFTRoyaltyToCoinBasisPoints", "HasUnlockable", "IsForSale"] } }, "create-nft-bid": { method: "POST", url: "/api/v0/create-nft-bid", handler: "CreateNFTBid", description: "Place a bid on an NFT", desoJs: "createNftBid", params: { required: ["UpdaterPublicKeyBase58Check", "NFTPostHashHex", "SerialNumber", "BidAmountNanos", "MinFeeRateNanosPerKB"], optional: [] } }, "accept-nft-bid": { method: "POST", url: "/api/v0/accept-nft-bid", handler: "AcceptNFTBid", description: "Accept a bid on an NFT", desoJs: "acceptNftBid", params: { required: ["UpdaterPublicKeyBase58Check", "NFTPostHashHex", "SerialNumber", "BidderPublicKeyBase58Check", "BidAmountNanos", "MinFeeRateNanosPerKB"], optional: ["UnlockableText"] } }, "transfer-nft": { method: "POST", url: "/api/v0/transfer-nft", handler: "TransferNFT", description: "Transfer NFT to another user", desoJs: "transferNft", params: { required: ["SenderPublicKeyBase58Check", "ReceiverPublicKeyBase58Check", "NFTPostHashHex", "SerialNumber", "MinFeeRateNanosPerKB"], optional: ["UnlockableText"] } }, "burn-nft": { method: "POST", url: "/api/v0/burn-nft", handler: "BurnNFT", description: "Burn/destroy an NFT", desoJs: "burnNft", params: { required: ["UpdaterPublicKeyBase58Check", "NFTPostHashHex", "SerialNumber", "MinFeeRateNanosPerKB"], optional: [] } } } }, dao: { description: "DAO Coin operations including minting, burning, transfers, and limit orders", backendFile: "routes/transaction.go", endpoints: { "dao-coin": { method: "POST", url: "/api/v0/dao-coin", handler: "DAOCoin", description: "Mint, burn, or disable minting for DAO coins", desoJs: "daoCoin", params: { required: ["UpdaterPublicKeyBase58Check", "ProfilePublicKeyBase58CheckOrUsername", "OperationType", "MinFeeRateNanosPerKB"], optional: ["CoinsToMintNanos", "CoinsToBurnNanos", "TransferRestrictionStatus"] } }, "transfer-dao-coin": { method: "POST", url: "/api/v0/transfer-dao-coin", handler: "TransferDAOCoin", description: "Transfer DAO coins between users", desoJs: "transferDAOCoin", params: { required: ["SenderPublicKeyBase58Check", "ProfilePublicKeyBase58CheckOrUsername", "ReceiverPublicKeyBase58CheckOrUsername", "DAOCoinToTransferNanos", "MinFeeRateNanosPerKB"], optional: [] } }, "create-dao-coin-limit-order": { method: "POST", url: "/api/v0/create-dao-coin-limit-order", handler: "CreateDAOCoinLimitOrder", description: "Create limit orders for DAO coin trading", desoJs: "createDAOCoinLimitOrder", params: { required: ["TransactorPublicKeyBase58Check", "BuyingDAOCoinCreatorPublicKeyBase58Check", "SellingDAOCoinCreatorPublicKeyBase58Check", "Price", "Quantity", "OperationType", "FillType", "MinFeeRateNanosPerKB"], optional: [] } }, "create-dao-coin-market-order": { method: "POST", url: "/api/v0/create-dao-coin-market-order", handler: "CreateDAOCoinMarketOrder", description: "Create market orders for DAO coin trading", desoJs: "createDAOCoinMarketOrder", params: { required: ["TransactorPublicKeyBase58Check", "BuyingDAOCoinCreatorPublicKeyBase58Check", "SellingDAOCoinCreatorPublicKeyBase58Check", "Quantity", "OperationType", "FillType", "MinFeeRateNanosPerKB"], optional: [] } }, "cancel-dao-coin-limit-order": { method: "POST", url: "/api/v0/cancel-dao-coin-limit-order", handler: "CancelDAOCoinLimitOrder", description: "Cancel existing DAO coin limit orders", desoJs: "cancelDAOCoinLimitOrder", params: { required: ["TransactorPublicKeyBase58Check", "CancelOrderID", "MinFeeRateNanosPerKB"], optional: [] } } } }, access: { description: "Access group management for DeSo messaging and permissions", backendFile: "routes/access_group.go", documentation: { endpoints: ["docs/deso-backend/api/access-group-endpoints.md"], transactions: ["docs/deso-backend/construct-transactions/access-groups-api.md"] }, endpoints: { "get-all-user-access-groups": { method: "POST", url: "/api/v0/get-all-user-access-groups", handler: "GetAllUserAccessGroups", description: "Get all access groups owned by and member of for a user", desoJs: "getAllUserAccessGroups", params: { required: ["PublicKeyBase58Check"], optional: [] } }, "check-party-access-groups": { method: "POST", url: "/api/v0/check-party-access-groups", handler: "CheckPartyAccessGroups", description: "Check if both sender and receiver have requested access groups", desoJs: "checkPartyAccessGroups", params: { required: [ "SenderPublicKeyBase58Check", "SenderAccessGroupKeyName", "RecipientPublicKeyBase58Check", "RecipientAccessGroupKeyName" ], optional: [] } }, "create-access-group": { method: "POST", url: "/api/v0/create-access-group", handler: "CreateAccessGroup", description: "Create a new access group for messaging", desoJs: "createAccessGroup", params: { required: [ "AccessGroupOwnerPublicKeyBase58Check", "AccessGroupPublicKeyBase58Check", "AccessGroupKeyName", "MinFeeRateNanosPerKB" ], optional: ["TransactionFees", "ExtraData"] } } } } }; if (endpoint) { for (const [cat, info] of Object.entries(apiDetails)) { if (info.endpoints[endpoint]) { const ep = info.endpoints[endpoint]; let result = `# ${endpoint}\n\n`; result += `**Category:** ${cat.toUpperCase()}\n`; result += `**Description:** ${ep.description}\n\n`; result += `**API Details:**\n`; result += `- Method: ${ep.method}\n`; result += `- URL: ${ep.url}\n`; result += `- Backend Handler: ${ep.handler}\n`; result += `- Backend File: ${info.backendFile}\n`; result += `- deso-js Function: ${ep.desoJs}\n\n`; result += `**Parameters:**\n`; result += `- Required: ${ep.params.required.join(', ') || 'None'}\n`; result += `- Optional: ${ep.params.optional.join(', ') || 'None'}\n\n`; if (includeCode) { result += generateEndpointExample(endpoint, ep); } return { content: [{ type: "text", text: result }] }; } } return { content: [{ type: "text", text: `Endpoint "${endpoint}" not found` }] }; } if (category === 'all') { let result = "# Complete DeSo API Reference\n\n"; for (const [cat, info] of Object.entries(apiDetails)) { result += `## ${cat.toUpperCase()} APIs\n`; result += `${info.description}\n`; result += `**Backend:** ${info.backendFile}\n\n`; for (const [ep, details] of Object.entries(info.endpoints)) { result += `### ${ep}\n`; result += `${details.description}\n`; result += `- ${details.method} ${details.url}\n`; result += `- deso-js: ${details.desoJs}\n\n`; } } return { content: [{ type: "text", text: result }] }; } if (apiDetails[category]) { const info = apiDetails[category]; let result = `# ${category.toUpperCase()} APIs\n\n`; result += `${info.description}\n\n`; result += `**Backend Implementation:** ${info.backendFile}\n\n`; for (const [ep, details] of Object.entries(info.endpoints)) { result += `## ${ep}\n`; result += `${details.description}\n\n`; result += `**API:** ${details.method} ${details.url}\n`; result += `**Handler:** ${details.handler}\n`; result += `**deso-js:** ${details.desoJs}\n\n`; result += `**Required Params:** ${details.params.required.join(', ') || 'None'}\n`; result += `**Optional Params:** ${details.params.optional.join(', ') || 'None'}\n\n`; } return { content: [{ type: "text", text: result }] }; } return { content: [{ type: "text", text: `Unknown category: ${category}` }] }; } async function getDesoJsGuide(args) { const { topic, framework = 'vanilla' } = args; // COMPREHENSIVE GUIDES from original mcp-server.js const guides = { setup: { title: "DeSo-JS SDK Setup & Installation", content: `# DeSo-JS SDK Setup ## Installation \`\`\`bash npm install deso-protocol \`\`\` ## Configuration \`\`\`javascript import { configure } from 'deso-protocol'; configure({ // Permissions for derived keys spendingLimitOptions: { GlobalDESOLimit: 1 * 1e9, // 1 DeSo in nanos TransactionCountLimitMap: { BASIC_TRANSFER: 2, SUBMIT_POST: 4, CREATE_FOLLOW_TXN_STATELESS: 10 } }, // Optional node URI (defaults to https://node.deso.org) nodeURI: 'https://node.deso.org', // Your app name appName: 'My DeSo App', // Fee rate MinFeeRateNanosPerKB: 1000 }); \`\`\` ## React Native Setup For React Native, provide storage and browser handlers: \`\`\`javascript import AsyncStorage from '@react-native-async-storage/async-storage'; import * as WebBrowser from 'expo-web-browser'; configure({ // ... other options storageProvider: AsyncStorage, identityPresenter: async (url) => { const result = await WebBrowser.openAuthSessionAsync(url); if (result.type === 'success') { identity.handleRedirectURI(result.url); } } }); \`\`\`` }, identity: { title: "Identity & Authentication with DeSo-JS", content: `# Identity & Authentication ## Basic Login/Logout \`\`\`javascript import { identity } from 'deso-protocol'; // Start login flow await identity.login(); // Logout await identity.logout(); // Subscribe to identity state changes identity.subscribe((state) => { console.log('Current user:', state.currentUser); console.log('Event:', state.event); console.log('Alternate users:', state.alternateUsers); }); \`\`\` ## Permission Management \`\`\`javascript // Check permissions (synchronous) const hasPermission = identity.hasPermissions({ TransactionCountLimitMap: { SUBMIT_POST: 1 } }); // Check permissions (async for React Native) const hasPermissionAsync = await identity.hasPermissionsAsync({ TransactionCountLimitMap: { SUBMIT_POST: 1 } }); // Request permissions if (!hasPermission) { await identity.requestPermissions({ TransactionCountLimitMap: { SUBMIT_POST: 1, CREATE_FOLLOW_TXN_STATELESS: 5 } }); } \`\`\` ## JWT for API Calls \`\`\`javascript // Generate JWT for authenticated requests const jwt = await identity.jwt(); // Use in API calls const response = await fetch('/api/authenticated-endpoint', { headers: { 'Authorization': \`Bearer \${jwt}\` } }); \`\`\`` }, transactions: { title: "Transaction Creation with DeSo-JS", content: `# Transaction Creation ## Social Transactions \`\`\`javascript import { submitPost, updateFollowingStatus, updateLikeStatus, sendDiamonds } from 'deso-protocol'; // Submit a post const postResult = await submitPost({ UpdaterPublicKeyBase58Check: currentUser.publicKey, BodyObj: { Body: 'Hello DeSo!', ImageURLs: [], VideoURLs: [] } }); // Follow a user const followResult = await updateFollowingStatus({ FollowerPublicKeyBase58Check: currentUser.publicKey, FollowedPublicKeyBase58Check: 'BC1YLi...', IsUnfollow: false }); // Like a post const likeResult = await updateLikeStatus({ ReaderPublicKeyBase58Check: currentUser.publicKey, LikedPostHashHex: 'abcd1234...', IsUnlike: false }); // Send diamonds const diamondResult = await sendDiamonds({ SenderPublicKeyBase58Check: currentUser.publicKey, ReceiverPublicKeyBase58Check: 'BC1YLi...', DiamondPostHashHex: 'abcd1234...', DiamondLevel: 1 }); \`\`\` ## Financial Transactions \`\`\`javascript import { buyCreatorCoin, sendDeso } from 'deso-protocol'; // Buy creator coin const buyResult = await buyCreatorCoin({ UpdaterPublicKeyBase58Check: currentUser.publicKey, CreatorPublicKeyBase58Check: 'BC1YLi...', DeSoToSellNanos: 1000000, // 0.001 DeSo MinCreatorCoinExpectedNanos: 100 }); // Send DeSo const sendResult = await sendDeso({ SenderPublicKeyBase58Check: currentUser.publicKey, RecipientPublicKeyOrUsername: 'username', AmountNanos: 1000000 // 0.001 DeSo }); \`\`\`` }, data: { title: "Data Fetching with DeSo-JS", content: `# Data Fetching ## Get Posts \`\`\`javascript import { getPostsStateless, getSinglePost, getPostsForUser } from 'deso-protocol'; // Get latest posts const posts = await getPostsStateless({ NumToFetch: 20 }); // Get single post const post = await getSinglePost({ PostHashHex: 'abcd1234...' }); // Get posts for specific user const userPosts = await getPostsForUser({ Username: 'nader', NumToFetch: 10 }); \`\`\` ## Get Users \`\`\`javascript import { getSingleProfile, getUsersStateless, getFollowersForUser } from 'deso-protocol'; // Get single profile const profile = await getSingleProfile({ Username: 'nader' }); // Get multiple users const users = await getUsersStateless({ PublicKeysBase58Check: ['BC1YLi...', 'BC1YLf...'] }); // Get followers const followers = await getFollowersForUser({ Username: 'nader' }); \`\`\`` } }; if (guides[topic]) { return { content: [{ type: "text", text: guides[topic].content }] }; } return { content: [{ type: "text", text: `Topic "${topic}" not found. Available: ${Object.keys(guides).join(', ')}` }] }; } async function generateDesoCode(args) { const { operation, language, includeAuth = false, fullExample = false } = args; const examples = { follow: { javascript: `// Follow a user using deso-js import { updateFollowingStatus${includeAuth ? ', identity' : ''} } from 'deso-protocol'; ${includeAuth ? `// Login first await identity.login(); const currentUser = identity.snapshot().currentUser; ` : ''} const result = await updateFollowingStatus({ FollowerPublicKeyBase58Check: ${includeAuth ? 'currentUser.publicKey' : "'YOUR_PUBLIC_KEY'"}, FollowedPublicKeyBase58Check: 'BC1YLiQ86kwXaVaUVwKyKF5uo2Kxt8SSeZ2CzGTAVp2TRb4VJeFHLqd', IsUnfollow: false }); console.log('Follow successful:', result);`, react: `// React Follow Button Component import React, { useState } from 'react'; import { updateFollowingStatus, identity } from 'deso-protocol'; function FollowButton({ userToFollow }) { const [isFollowing, setIsFollowing] = useState(false); const [loading, setLoading] = useState(false); const handleFollow = async () => { setLoading(true); try { const currentUser = identity.snapshot().currentUser; await updateFollowingStatus({ FollowerPublicKeyBase58Check: currentUser.publicKey, FollowedPublicKeyBase58Check: userToFollow.PublicKeyBase58Check, IsUnfollow: isFollowing }); setIsFollowing(!isFollowing); } catch (error) { console.error('Follow error:', error); } finally { setLoading(false); } }; return ( <button onClick={handleFollow} disabled={loading}> {loading ? 'Loading...' : isFollowing ? 'Unfollow' : 'Follow'} </button> ); }` }, post: { javascript: `// Create a post using deso-js import { submitPost${includeAuth ? ', identity' : ''} } from 'deso-protocol'; ${includeAuth ? `// Login first await identity.login(); const currentUser = identity.snapshot().currentUser; ` : ''} const result = await submitPost({ UpdaterPublicKeyBase58Check: ${includeAuth ? 'currentUser.publicKey' : "'YOUR_PUBLIC_KEY'"}, BodyObj: { Body: 'Hello DeSo! This is my first post using the SDK.', ImageURLs: [], VideoURLs: [] } }); console.log('Post created:', result);` } }; const code = examples[operation]?.[language] || `// ${operation} example in ${language} // Generated code for DeSo ${operation} operation console.log('${operation} implementation here');`; return { content: [{ type: "text", text: `# DeSo Code Generator **Operation:** ${operation} **Language:** ${language} **Include Auth:** ${includeAuth} ## Generated Code: \`\`\`${language} ${code} \`\`\` Your DeSo MCP server is generating code! ⚡` }] }; } async function explainDesoArchitecture(args) { const { topic, includeCode = false } = args; // Enhanced architecture explanations with repository context const architectureTopics = { "transaction-flow": `# DeSo Transaction Architecture ## Two-Phase Transaction System DeSo implements a unique two-phase transaction system for security and usability: ### Phase 1: Transaction Construction - **Location:** Backend API (\`repos/backend/routes/\`) - **Purpose:** Create unsigned transaction with proper fees and validation - **Components:** - Transaction handlers in \`routes/transaction.go\` - Fee calculation in \`core/lib/transaction_fees.go\` - Validation in \`core/lib/block_view.go\` ### Phase 2: Transaction Signing & Submission - **Location:** Identity Service (\`repos/identity/\`) - **Purpose:** Sign transaction with user's private key - **Components:** - Key management in Identity iframe - Derived key authorization - Transaction submission to mempool ## Repository Structure: - \`repos/backend/\` - Go backend with transaction handlers - \`repos/identity/\` - TypeScript identity service - \`repos/deso-js/\` - JavaScript SDK abstraction - \`repos/frontend/\` - Reference frontend implementation`, "identity-system": `# DeSo Identity Architecture ## Identity Service Components ### Core Identity (\`repos/identity/\`) The identity service manages user authentication and key storage: - **Iframe Communication**: Secure cross-origin messaging - **Key Storage**: Encrypted local storage of private keys - **Derived Keys**: Limited permission keys for applications - **Access Levels**: Granular permission system ### Integration with deso-js (\`repos/deso-js/\`) The SDK provides seamless identity integration: \`\`\`typescript // Identity abstraction in deso-js import { identity } from 'deso-protocol'; // Login flow await identity.login(); const user = identity.snapshot().currentUser; // Permission management await identity.requestPermissions({ TransactionCountLimitMap: { SUBMIT_POST: 5 } }); \`\`\` ## Documentation References: - Tutorial: \`repos/docs/deso-tutorial-build-apps.md\` - Identity Docs: \`repos/docs/deso-identity/\` - Architecture: \`repos/docs/architecture-overview/\``, "backend-implementation": `# DeSo Backend Architecture ## Go Backend Structure (\`repos/backend/\`) ### Core Components: - \`main.go\` - Entry point and server setup - \`routes/\` - API endpoint handlers - \`apis/\` - External API integrations - \`config/\` - Configuration management ### Key Route Files: - \`routes/transaction.go\` - Transaction construction - \`routes/post.go\` - Post-related endpoints - \`routes/user.go\` - User profile endpoints - \`routes/nft.go\` - NFT marketplace endpoints - \`routes/associations.go\` - Association system ### Core Library (\`repos/core/\`) Shared blockchain logic: - Transaction types and validation - Block processing and consensus - Mempool management - Fee calculation ## API Integration: The backend integrates with \`deso-js\` through standardized endpoints that match SDK function calls.`, "frontend-patterns": `# DeSo Frontend Architecture ## Reference Implementation (\`repos/frontend/\`) The reference frontend demonstrates best practices: ### Component Structure: - Identity integration components - Transaction confirmation flows - Real-time data fetching patterns - Error handling and loading states ### Key Patterns: - React hooks for DeSo state management - TypeScript for type safety - Responsive design principles - Performance optimization ## Integration with deso-js: The frontend shows practical usage of the SDK for common operations like posting, following, and transactions. ### Documentation: - Frontend docs: \`repos/docs/deso-frontend/\` - Tutorial: \`repos/docs/deso-tutorial-build-apps.md\` - Applications guide: \`repos/docs/deso-applications.md\``, "messaging-architecture": `# DeSo Messaging Architecture (Access Groups) ## Modern Messaging System (from deso-chat analysis) DeSo implements end-to-end encrypted messaging using Access Groups: ### Core Components: 1. **Access Groups**: Encryption containers for messages 2. **Derived Keys**: Handle encryption without identity popups 3. **Message APIs**: Modern endpoints for DM and group chat 4. **Real-time Polling**: Live message updates ### Access Group Pattern: \`\`\`typescript // Auto-setup default messaging access group const { AccessGroupsOwned } = await getAllAccessGroups({ PublicKeyBase58Check: userPublicKey }); if (!AccessGroupsOwned?.find(g => g.AccessGroupKeyName === "default-key")) { await createAccessGroup({ AccessGroupOwnerPublicKeyBase58Check: userPublicKey, AccessGroupPublicKeyBase58Check: messagingPublicKey, AccessGroupKeyName: "default-key", MinFeeRateNanosPerKB: 1000 }); } \`\`\` ### Message Flow: 1. **Check Access Groups**: Verify sender/receiver have required groups 2. **Encrypt Message**: Use recipient's access group public key 3. **Send Message**: Via sendDMMessage or sendGroupChatMessage 4. **Retrieve Messages**: Use getAllMessageThreads + pagination 5. **Decrypt Messages**: Use identity.decryptMessage with access groups ### Key APIs: - \`getAllMessageThreads\` - Get conversation list - \`getPaginatedDMThread\` - Get DM messages with pagination - \`getPaginatedGroupChatThread\` - Get group messages with pagination - \`sendDMMessage\` - Send encrypted direct message - \`sendGroupChatMessage\` - Send encrypted group message ### Implementation Patterns: - **Spending Limits**: Set unlimited NEW_MESSAGE transactions - **Error Handling**: Retry access group fetching if decryption fails - **Mobile Optimization**: Longer polling intervals (20s vs 5s) - **State Management**: Conversation maps with message arrays - **Real-time Updates**: Polling with conversation-specific updates` }; let response; if (architectureTopics[topic]) { response = architectureTopics[topic]; } else { // Default comprehensive overview response = `# DeSo Architecture Overview: ${topic} ## Complete System Architecture DeSo is a decentralized social blockchain with several key repositories: ### 1. Backend (\`repos/backend/\`) - **Language:** Go - **Purpose:** API server, transaction construction, data serving - **Key Files:** \`routes/\`, \`main.go\`, \`config/\` ### 2. Core (\`repos/core/\`) - **Language:** Go - **Purpose:** Blockchain consensus, transaction validation - **Key Files:** Block processing, mempool, fees ### 3. Identity (\`repos/identity/\`) - **Language:** TypeScript/Angular - **Purpose:** User authentication, key management - **Key Files:** Identity service, key derivation ### 4. Frontend (\`repos/frontend/\`) - **Language:** TypeScript/Angular - **Purpose:** Reference web application - **Key Features:** Social feeds, messaging, creator coins ### 5. deso-js (\`repos/deso-js/\`) - **Language:** TypeScript - **Purpose:** Developer SDK for easy integration - **Key Features:** Transaction abstraction, identity integration ### 6. Documentation (\`repos/docs/\`) - **Format:** Markdown - **Content:** Tutorials, API docs, architecture guides - **Key Files:** Build tutorial, tokenomics, governance ## Integration Flow: Frontend/Apps → deso-js SDK → Backend API → Core Blockchain For specific topics, try: "transaction-flow", "identity-system", "backend-implementation", or "frontend-patterns"`; } // Add repository search suggestion response += `\n\n## 🔍 Explore Further Use the \`repository_search\` tool to find specific documentation: - Search "transaction" for transaction-related docs - Search "identity" for authentication guides - Search "tutorial" for step-by-step guides - Search "API" for endpoint documentation Or use \`read_repository_document\` to read specific files like: - \`docs/deso-tutorial-build-apps.md\` - \`docs/architecture-overview/README.md\` - \`deso-js/README.md\``; if (includeCode) { if (topic === "messaging-architecture") { response += `\n\n## Complete Messaging Implementation Example \`\`\`typescript // DeSo Messaging Setup (from deso-chat analysis) import { configure, identity, getAllAccessGroups, createAccessGroup, checkPartyAccessGroups, sendDMMessage, getAllMessageThreads, getPaginatedDMThread, DeSoNetwork, NOTIFICATION_EVENTS } from 'deso-protocol'; // 1. Configure SDK with messaging permissions configure({ identityURI: process.env.REACT_APP_IDENTITY_URL, nodeURI: process.env.REACT_APP_NODE_URL, network: DeSoNetwork.mainnet, spendingLimitOptions: { GlobalDESOLimit: 5 * 1e9, TransactionCountLimitMap: { AUTHORIZE_DERIVED_KEY: 1, NEW_MESSAGE: "UNLIMITED" }, AccessGroupLimitMap: [{ AccessGroupOwnerPublicKeyBase58Check: "", ScopeType: "Any", AccessGroupKeyName: "", OperationType: "Any", OpCount: "UNLIMITED" }] } }); // 2. Setup user with auto-created access groups identity.subscribe(async ({ event, currentUser }) => { if (event === NOTIFICATION_EVENTS.LOGIN_END && currentUser) { const { messagingPublicKeyBase58Check } = currentUser.primaryDerivedKey; // Get existing access groups const { AccessGroupsOwned } = await getAllAccessGroups({ PublicKeyBase58Check: currentUser.publicKey }); // Auto-create default messaging group if needed if (!AccessGroupsOwned?.find(g => g.AccessGroupKeyName === "default-key")) { await createAccessGroup({ AccessGroupOwnerPublicKeyBase58Check: currentUser.publicKey, AccessGroupPublicKeyBase58Check: messagingPublicKeyBase58Check, AccessGroupKeyName: "default-key", MinFeeRateNanosPerKB: 1000 }); } } }); // 3. Send encrypted message async function sendMessage(recipientPublicKey: string, messageText: string) { const currentUser = identity.snapshot().currentUser; // Check both parties have required access groups const response = await checkPartyAccessGroups({ SenderPublicKeyBase58Check: currentUser.publicKey, SenderAccessGroupKeyName: "default-key", RecipientPublicKeyBase58Check: recipientPublicKey, RecipientAccessGroupKeyName: "default-key" }); // Encrypt message const encryptedMessage = await identity.encryptMessage( response.RecipientAccessGroupPublicKeyBase58Check, messageText ); // Send message const result = await sendDMMessage({ SenderAccessGroupOwnerPublicKeyBase58Check: currentUser.publicKey, SenderAccessGroupPublicKeyBase58Check: response.SenderAccessGroupPublicKeyBase58Check, SenderAccessGroupKeyName: "default-key", RecipientAccessGroupOwnerPublicKeyBase58Check: recipientPublicKey, RecipientAccessGroupPublicKeyBase58Check: response.RecipientAccessGroupPublicKeyBase58Check, RecipientAccessGroupKeyName: "default-key", EncryptedMessageText: encryptedMessage, MinFeeRateNanosPerKB: 1000 }); return result.submittedTransactionResponse.TxnHashHex; } // 4. Get conversations and messages async function getConversations(userPublicKey: string) { // Get all conversation threads const threads = await getAllMessageThreads({ UserPublicKeyBase58Check: userPublicKey }); // Get access groups for decryption const { AccessGroupsOwned, AccessGroupsMember } = await getAllAccessGroups({ PublicKeyBase58Check: userPublicKey }); const allAccessGroups = (AccessGroupsOwned || []).concat(AccessGroupsMember || []); // Decrypt messages const decryptedMessages = await Promise.all( threads.MessageThreads.map(m => identity.decryptMessage(m, allAccessGroups)) ); return { conversations: organizeIntoConversations(decryptedMessages), profiles: threads.PublicKeyToProfileEntryResponse }; } // 5. Real-time polling pattern function startMessagePolling(userPublicKey: string, callback: Function) { const pollInterval = setInterval(async () => { try { const { conversations } = await getConversations(userPublicKey); callback(conversations); } catch (error) { console.error('Polling error:', error); } }, 5000); // 5 second intervals return () => clearInterval(pollInterval); } \`\`\``; } else { response += `\n\n## Code Example \`\`\`javascript // Complete DeSo integration example import { identity, submitPost } from 'deso-protocol'; // 1. Identity phase await identity.login(); const user = identity.snapshot().currentUser; // 2. Transaction phase (SDK handles backend + signing) const result = await submitPost({ UpdaterPublicKeyBase58Check: user.publicKey, BodyObj: { Body: 'Built with DeSo architecture!' } }); console.log('Transaction:', result.TransactionIDBase58Check); \`\`\``; } } return { content: [{ type: "text", text: response }] }; } // Helper function from original comprehensive server function generateEndpointExample(endpoint, details) { return ` ## Code Examples ### Using deso-js SDK \`\`\`javascript import { ${details.desoJs} } from 'deso-protocol'; const result = await ${details.desoJs}({ ${details.params.required.map(p => `${p}: 'value'`).join(',\n ')} }); \`\`\` ### Direct API Call \`\`\`javascript const response = await fetch('https://node.deso.org${details.url}', { method: '${details.method}', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ${details.params.required.map(p => `${p}: 'value'`).join(',\n ')} }) }); \`\`\` `; } async function repositorySearch(args) { const { query } = args; try { const results = await searchRepositoryDocuments(query); if (results.length === 0) { return { content: [{ type: "text", text: `# Repository Search Results **Query:** "${query}" No matching documents found in the DeSo repository. **Available repositories:** docs, core, identity, frontend, backend, deso-js` }] }; } let response = `# Repository Search Results\n\n**Query:** "${query}"\n**Found:** ${results.length} matches\n\n`; for (const result of results.slice(0, 10)) { // Limit to top 10 results response += `## ${result.title}\n`; response += `**Path:** \`${result.path}\`\n`; response += `**Repository:** ${result.repository}\n\n`; response += `${result.excerpt}\n\n---\n\n`; } if (results.length > 10) { response += `*Showing top 10 of ${results.length} results*\n`; } return { content: [{ type: "text", text: response }] }; } catch (error) { console.error("Repository search error:", error); return { content: [{ type: "text", text: `# Repository Search Error **Query:** "${query}" Error searching repository: ${error.message} This might be due to repository access permissions or path issues.` }] }; } } // Repository document search implementation async function searchRepositoryDocuments(query) { const repositories = ['docs', 'core', 'identity', 'frontend', 'backend', 'deso-js', 'deso-chat', 'deso-ui', 'graphql']; const reposPath = path.join(process.cwd(), 'repos'); const results = []; const searchTerms = query.toLowerCase().split(' '); for (const repo of repositories) { const repoPath = path.join(reposPath, repo); try { const stats = await fs.stat(repoPath); if (stats.isDirectory()) { const repoResults = await searchInDirectory(repoPath, repo, searchTerms); results.push(...repoResults); } } catch (error) { console.error(`Error accessing ${repo}:`, error.message); continue; } } // Sort by relevance (number of matching terms) return results.sort((a, b) => b.score - a.score); } async function searchInDirectory(dirPath, repository, searchTerms) { const results = []; try { const entries = await fs.readdir(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules' && entry.name !== '.next' && entry.name !== 'dist' && entry.name !== 'build' && entry.name !== 'storybook-static') { // Recursively search subdirectories const subResults = await searchInDirectory(fullPath, repository, searchTerms); results.push(...subResults); } else if (entry.isFile() && isSearchableFile(entry.name)) { try { const content = await fs.readFile(fullPath, 'utf-8'); const matches = findMatches(content, searchTerms, fullPath, repository); results.push(...matches); } catch (error) { // Skip files that can't be read continue; } } } } catch (error) { console.error(`Error reading directory ${dirPath}:`, error.message); } return results; } function isSearchableFile(filename) { const searchableExtensions = [ '.md', '.txt', '.js', '.ts', '.go', '.json', '.yaml', '.yml', '.tsx', '.jsx', '.graphql', '.gql', '.css', '.scss', '.html', '.vue', '.py', '.sh', '.env', '.toml', '.ini', '.conf', '.config', '.lock' ]; const ext = path.extname(filename).toLowerCase(); return searchableExtensions.includes(ext) || filename === 'README' || filename === 'LICENSE'; } function findMatches(content, searchTerms, filePath, repository) { const lines = content.split('\n'); const matches = []; const contentLower = content.toLowerCase(); // Calculate relevance score let score = 0; for (const term of searchTerms) { const termCount = (contentLower.match(new RegExp(term, 'g')) || []).length; score += termCount; } if (score === 0) return []; // Find relevant excerpts const relevantLines = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineLower = line.toLowerCase(); if (searchTerms.some(term => lineLower.includes(term))) { // Include context lines const start = Math.max(0, i - 2); const end = Math.min(lines.length, i + 3); relevantLines.push({ lineNumber: i + 1, content: lines.slice(start, end).join('\n'), matchLine: line }); } } // Create title from file path and content const fileName = path.basename(filePath); const title = getDocumentTitle(content, fileName); // Create excerpt from best matches const excerpt = createExcerpt(relevantLines, searchTerms); matches.push({ title, path: path.relative(path.join(process.cwd(), 'repos'), filePath), repository, score, excerpt, matchCount: relevantLines.length }); return matches; } function getDocumentTitle(content, fileName) { // Try to extract title from markdown headers const lines = content.split('\n'); for (const line of lines.slice(0, 10)) { // Check first 10 lines if (line.startsWith('# ')) { return line.substring(2).trim(); } } // Fallback to filename return fileName.replace(path.extname(fileName), '').replace(/[-_]/g, ' '); } function createExcerpt(relevantLines, searchTerms) { if (relevantLines.length === 0) return ''; // Take the best matching excerpt const bestMatch = relevantLines[0]; let excerpt = bestMatch.content; // Highlight search terms for (const term of searchTerms) { const regex = new RegExp(`(${term})`, 'gi'); excerpt = excerpt.replace(regex, '**$1**'); } // Limit excerpt length if (excerpt.length > 500) { excerpt = excerpt.substring(0, 500) + '...'; } return excerpt; } // Repository document reader async function readRepositoryDocument(args) { const { path: docPath, repository } = args; try { let fullPath; if (repository) { fullPath = path.join(process.cwd(), 'repos', repository, docPath); } else { // If no repository specified, check if path already includes it fullPath = path.join(process.cwd(), 'repos', docPath); } const content = await fs.readFile(fullPath, 'utf-8'); const fileName = path.basename(fullPath); const title = getDocumentTitle(content, fileName); return { content: [{ type: "text", text: `# ${title} **Path:** \`${docPath}\` **Repository:** ${repository || 'auto-detected'} **File:** ${fileName} --- ${content}` }] }; } catch (error) { console.error("Document read error:", error); return { content: [{ type: "text", text: `# Document Read Error **Path:** "${docPath}" **Repository:** ${repository || 'auto-detect'} Error reading document: ${error.message} **Available repositories:** docs, core, identity, frontend, backend, deso-js, deso-chat, deso-ui, graphql Try using the \`repository_search\` tool first to find the correct document path.` }] }; } } // Comprehensive DeSo debugging guide based on real debugging experience async function desoDebuggingGuide(args) { const { issue, includeCode = false } = args; const debuggingGuides = { "message-decryption": { title: "🔐 Message Decryption Issues", content: `# Message Decryption Debugging ## 🚨 CRITICAL: DecryptedMessage vs MessageText **The #1 Issue:** Using wrong property name for decrypted messages ### ❌ WRONG (Common Mistake) \`\`\`javascript if (decryptedMessage.MessageText) { messageText = decryptedMessage.MessageText; // This is WRONG! } \`\`\` ### ✅ CORRECT (Actual Fix) \`\`\`javascript if (decryptedMessage.DecryptedMessage) { messageText = decryptedMessage.DecryptedMessage; // This is RIGHT! } \`\`\` ## 🔍 Debugging Steps 1. **Check Console Logs**: Look for \`hasMessageText: false\` vs \`hasDecryptedMessage: true\` 2. **Inspect Response Structure**: Use \`Object.keys(decryptedMessage)\` to see available properties 3. **Reference Implementation**: Always check deso-chat for correct patterns ## 🛠️ Complete Decryption Pattern \`\`\`javascript // Proper message decryption handling for (const decryptedMessage of decrypted) { let messageText = '[Encrypted Message]'; if (decryptedMessage.error) { messageText = '[Decryption Failed]'; } else if (decryptedMessage.DecryptedMessage) { // ✅ CORRECT: Use DecryptedMessage property messageText = decryptedMessage.DecryptedMessage; } else if (decryptedMessage.MessageInfo?.ExtraData?.unencrypted === "true") { // Handle unencrypted messages (hex-encoded) try { const hexString = decryptedMessage.MessageInfo.EncryptedText; const bytes = new Uint8Array(Buffer.from(hexString, 'hex')); messageText = new TextDecoder().decode(bytes); } catch (error) { messageText = '[Decoding Failed]'; } } } \`\`\` ## 🔧 Common Error Messages - **"incorrect MAC"**: Normal for old/incompatible messages - handle gracefully - **"access group key not found"**: Need to fetch updated access groups - **Empty DecryptedMessage**: Check if message is unencrypted (ExtraData.unencrypted)` }, "access-groups": { title: "🔑 Access Group Issues", content: `# Access Group Debugging ## 🚨 CRITICAL: Empty String vs Undefined **The Issue:** \`checkPartyAccessGroups\` returns empty strings, not undefined! ### ❌ WRONG (Common Mistake) \`\`\`javascript if (response.SenderAccessGroupKeyName) { // This fails when API returns empty string! } \`\`\` ### ✅ CORRECT (Actual Fix) \`\`\`javascript const senderKeyName = response.SenderAccessGroupKeyName || DEFAULT_KEY_MESSAGING_GROUP_NAME; const recipientKeyName = response.RecipientAccessGroupKeyName || DEFAULT_KEY_MESSAGING_GROUP_NAME; \`\`\` ## 🔍 Access Group Flow 1. **Check Party Access Groups**: Always check both sender and recipient 2. **Handle Empty Responses**: API returns empty strings when groups don't exist 3. **Fallback to Default**: Use "default-key" as fallback 4. **Auto-Create Groups**: Create default messaging groups if needed ## 🛠️ Complete Access Group Pattern \`\`\`javascript // Proper access group handling const response = await checkPartyAccessGroups({ SenderPublicKeyBase58Check: senderPublicKey, SenderAccessGroupKeyName: DEFAULT_KEY_MESSAGING_GROUP_NAME, RecipientPublicKeyBase58Check: recipientPublicKey, RecipientAccessGroupKeyName: DEFAULT_KEY_MESSAGING_GROUP_NAME }); // ✅ CORRECT: Handle empty strings properly const senderKeyName = response.SenderAccessGroupKeyName || DEFAULT_KEY_MESSAGING_GROUP_NAME; const recipientKeyName = response.RecipientAccessGroupKeyName || DEFAULT_KEY_MESSAGING_GROUP_NAME; if (!senderKeyName || !recipientKeyName) { throw new Error('Access groups not available for messaging'); } \`\`\` ## 🔧 Error Messages to Watch For - **"SenderAccessGroupKeyName is undefined"**: Check for empty string handling - **"access group key not found"**: Need to create or fetch access groups - **"insufficient permissions"**: Check derived key permissions for access groups` }, "infinite-loops": { title: "🔄 Infinite Loop Issues", content: `# Infinite Loop Debugging ## 🚨 CRITICAL: useCallback Dependencies **The Issue:** Missing or incorrect dependencies in useCallback hooks ### ❌ WRONG (Causes Infinite Loops) \`\`\`javascript const searchUsers = useCallback(async (query) => { // Missing dependencies! }, []); // Empty dependency array is often wrong \`\`\` ### ✅ CORRECT (Prevents Infinite Loops) \`\`\`javascript const searchUsers = useCallback(async (query) => { if (!query.trim()) return; // Implementation... }, [getSingleProfile, setUsers, setLoading]); // Include ALL dependencies \`\`\` ## 🔍 Debugging Steps 1. **Check React DevTools**: Look for "Maximum update depth exceeded" 2. **Add Dependency Warnings**: Use ESLint react-hooks/exhaustive-deps 3. **Add Debouncing**: Prevent rapid API calls 4. **Isolate State Updates**: Separate loading states from data states ## 🛠️ Complete Debounced Search Pattern \`\`\`javascript // Proper debounced search with correct dependencies const [searchQuery, setSearchQuery] = useState(''); const [debouncedQuery, setDebouncedQuery] = useState(''); // Debounce the search query useEffect(() => { const timer = setTimeout(() => { setDebouncedQuery(searchQuery); }, 500); // 500ms debounce return () => clearTimeout(timer); }, [searchQuery]); // Search function with proper dependencies const searchUsers = useCallback(async (query) => { if (!query.trim()) { setUsers([]); return; } setLoading(true); try { const profile = await getSingleProfile({ Username: query }); setUsers(profile ? [profile] : []); } catch (error) { setError(error.message); setUsers([]); } finally { setLoading(false); } }, [getSingleProfile]); // Only include stable dependencies // Trigger search when debounced query changes useEffect(() => { if (debouncedQuery) { searchUsers(debouncedQuery); } }, [debouncedQuery, searchUsers]); \`\`\` ## 🔧 Common Patterns That Cause Loops - **Missing useCallback**: Functions recreated on every render - **Incorrect dependencies**: Including unstable objects/functions - **State updates in render**: Causing immediate re-renders - **Effect cleanup issues**: Not properly cleaning up timers/subscriptions` }, "api-responses": { title: "📡 API Response Structure Issues", content: `# API Response Structure Debugging ## 🚨 CRITICAL: Different Endpoints, Different Structures **The Issue:** Each DeSo endpoint has different response structures! ### Key Response Differences: - \`getAllMessageThreads\` → \`MessageThreads\` array - \`getPaginatedDMThread\` → \`ThreadMessages\` array - \`getAllAccessGroups\` → \`AccessGroupsOwned\` + \`AccessGroupsMember\` ## 🔍 Debugging API Responses ### 1. Always Log Response Structure \`\`\`javascript const response = await getPaginatedDMThread(params); console.log('API Response keys:', Object.keys(response)); console.log('Messages array:', response.ThreadMessages?.length); \`\`\` ### 2. Handle Empty/Null Responses \`\`\`javascript // ✅ CORRECT: Always validate response structure const messages = response?.ThreadMessages || []; const profiles = response?.PublicKeyToProfileEntryResponse || {}; \`\`\` ### 3. Check for JSON Parsing Errors \`\`\`javascript // ✅ CORRECT: Validate before parsing if (!response || response.trim() === '') { throw new Error('Empty response from API'); } try { const data = JSON.parse(response); return data; } catch (error) { throw new Error(\`Invalid JSON response: \${error.message}\`); } \`\`\` ## 🛠️ Complete API Error Handling Pattern \`\`\`javascript async function safeApiCall(apiFunction, params, fallback = null) { try { const response = await apiFunction(params); // Validate response structure if (!response || typeof response !== 'object') { throw new Error('Invalid API response structure'); } return response; } catch (error) { console.error('API call failed:', error); // Categorize errors for user-friendly messages if (error.message.includes('404')) { throw new Error('User not found'); } else if (error.message.includes('network')) { throw new Error('Network connection issue'); } else { throw new Error(\`API error: \${error.message}\`); } } } \`\`\` ## 🔧 Common API Issues - **"Cannot read property of undefined"**: Missing null checks - **"Unexpected end of JSON input"**: Empty response handling - **"404 errors"**: User/resource not found - handle gracefully - **Rate limiting**: Implement proper retry logic with exponential backoff` }, "authentication": { title: "🔐 Authentication & User Switching Issues", content: `# Authentication Debugging ## 🚨 CRITICAL: Identity Event Handling **The Issue:** Not properly handling identity state changes and user switching ### ✅ CORRECT: Complete Identity Setup \`\`\`javascript // Proper identity subscription with user switching identity.subscribe(({ currentUser, alternateUsers, event }) => { console.log('Identity event:', event); if (event === 'LOGIN_END' && currentUser) { setUser({ isAuthenticated: true, username: currentUser.username || currentUser.publicKey?.slice(0, 10) + '...', publicKey: currentUser.publicKey, alternateUsers: alternateUsers || [] }); } else if (event === 'LOGOUT') { setUser({ isAuthenticated: false, username: null, publicKey: null, alternateUsers: [] }); } }); \`\`\` ## 🔍 User Switching Implementation ### Account Switcher Component \`\`\`javascript function AccountSwitcher({ currentUser, alternateUsers }) { const [isOpen, setIsOpen] = useState(false); const handleSwitchUser = async (publicKey) => { try { await identity.setActiveUser(publicKey); setIsOpen(false); } catch (error) { console.error('Failed to switch user:', error); } }; return ( <div className="relative"> <button onClick={() => setIsOpen(!isOpen)}> {currentUser.username} <ChevronDownIcon /> </button> {isOpen && ( <div className="absolute right-0 mt-2 bg-white shadow-lg rounded-lg"> {alternateUsers.map(user => ( <button key={user.publicKey} onClick={() => handleSwitchUser(user.publicKey)} className="block w-full text-left px-4 py-2 hover:bg-gray-100" > {user.username || user.publicKey.slice(0, 10) + '...'} </button> ))} <hr /> <button onClick={() => identity.login()} className="block w-full text-left px-4 py-2 hover:bg-gray-100" > Add Another Account </button> <button onClick={() => identity.logout()} className="block w-full text-left px-4 py-2 hover:bg-gray-100 text-red-600" > Logout </button> </div> )} </div> ); } \`\`\` ## 🔧 Common Authentication Issues - **Identity not showing**: Check if LoginButton is in the header when authenticated - **User switching not working**: Ensure proper identity.setActiveUser() usage - **Permissions issues**: Check derived key permissions for messaging/transactions - **State not updating**: Verify identity.subscribe() is properly set up` }, "react-errors": { title: "⚛️ React-Specific Issues", content: `# React Integration Debugging ## 🚨 CRITICAL: Key Warnings & State Management ### 1. Unique Key Generation \`\`\`javascript // ❌ WRONG: Non-unique keys {messages.map((msg, index) => ( <div key={index}> // Don't use array index! ))} // ✅ CORRECT: Unique, stable keys {messages.map((msg) => ( <div key={\`\${msg.timestampNanos}-\${msg.senderPublicKey}\`}> ))} \`\`\` ### 2. Proper Loading State Management \`\`\`javascript // ✅ CORRECT: Separate loading states const [isLoading, setIsLoading] = useState(false); const [isLoadingMessages, setIsLoadingMessages] = useState(false); const [isLoadingConversations, setIsLoadingConversations] = useState(false); \`\`\` ### 3. Error Boundary Implementation \`\`\`javascript class DeSoErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { console.error('DeSo Error:', error, errorInfo); } render() { if (this.state.hasError) { return ( <div className="error-fallback"> <h2>Something went wrong with DeSo integration</h2> <details> {this.state.error?.message} </details> <button onClick={() => this.setState({ hasError: false, error: null })}> Try Again </button> </div> ); } return this.props.children; } } \`\`\` ## 🔧 Common React Issues - **"Maximum update depth exceeded"**: Check useCallback dependencies - **"Cannot read property of undefined"**: Add proper null checks - **"Each child should have unique key"**: Use stable, unique identifiers - **State not updating**: Verify proper state setter usage and dependencies` } }; if (issue === 'all') { let response = `# 🛠️ Complete DeSo Debugging Guide Based on real debugging experience and fixes applied during development. ## 🎯 Quick Reference | Issue | Key Fix | Common Error | |-------|---------|--------------| | Message Decryption | Use \`DecryptedMessage\` not \`MessageText\` | "[Encrypted Message]" showing | | Access Groups | Handle empty strings, not undefined | "SenderAccessGroupKeyName is undefined" | | Infinite Loops | Fix useCallback dependencies | "Maximum update depth exceeded" | | API Responses | Check response structure per endpoint | "Cannot read property of undefined" | | Authentication | Proper identity.subscribe() setup | User switching not working | | React Errors | Unique keys, proper state management | Key warnings, state issues | ## 🔍 Debugging Methodology 1. **Check Console Logs**: Look for specific error patterns 2. **Inspect Response Structure**: Use \`Object.keys()\` to see available properties 3. **Reference deso-chat**: Always check reference implementation 4. **Add Comprehensive Logging**: Use emoji-coded logging for easy identification 5. **Test Edge Cases**: Empty responses, failed API calls, network issues `; for (const [key, guide] of Object.entries(debuggingGuides)) { response += `\n## ${guide.title}\n\n`; response += guide.content.split('\n').slice(2, 8).join('\n'); // Summary response += `\n\n*Use \`deso_debugging_guide\` with issue="${key}" for complete details*\n\n`; } return { content: [{ type: "text", text: response }] }; } if (debuggingGuides[issue]) { const guide = debuggingGuides[issue]; let response = guide.content; if (includeCode) { response += `\n\n## 🧪 Testing & Validation ### Debug Logging Pattern \`\`\`javascript // Emoji-coded logging for easy debugging const log = { info: (category, message, data) => console.log(\`🔵 [\${category}] \${message}\`, data), warn: (category, message, data) => console.warn(\`⚠️ [\${category}] \${message}\`, data), error: (category, message, data) => console.error(\`❌ [\${category}] \${message}\`, data), debug: (category, message, data) => console.debug(\`🔍 [\${category}] \${message}\`, data), success: (category, message, data) => console.log(\`✅ [\${category}] \${message}\`, data) }; \`\`\` ### Validation Helpers \`\`\`javascript // Helper functions for common validations const validateApiResponse = (response, expectedKeys) => { if (!response || typeof response !== 'object') { throw new Error('Invalid API response'); } for (const key of expectedKeys) { if (!(key in response)) { console.warn(\`Missing expected key: \${key}\`); } } return response; }; const validatePublicKey = (publicKey) => { if (!publicKey || typeof publicKey !== 'string' || publicKey.length < 50) { throw new Error('Invalid public key format'); } return publicKey; }; \`\`\``; } return { content: [{ type: "text", text: response }] }; } return { content: [{ type: "text", text: `Unknown issue: ${issue}. Available: ${Object.keys(debuggingGuides).join(', ')}` }] }; } // Implementation patterns based on deso-chat analysis and debugging experience async function desoImplementationPatterns(args) { const { pattern, framework = 'react' } = args; const patterns = { "messaging-flow": { title: "📱 Complete Messaging Flow Pattern", content: `# DeSo Messaging Implementation Pattern ## 🎯 Complete Flow (from deso-chat analysis) ### 1. SDK Configuration with Messaging Permissions \`\`\`javascript import { configure, DeSoNetwork } from 'deso-protocol'; configure({ identityURI: process.env.REACT_APP_IDENTITY_URL, nodeURI: process.env.REACT_APP_NODE_URL, network: DeSoNetwork.mainnet, spendingLimitOptions: { GlobalDESOLimit: 5 * 1e9, // 5 DeSo TransactionCountLimitMap: { AUTHORIZE_DERIVED_KEY: 1, NEW_MESSAGE: "UNLIMITED" // ✅ CRITICAL: Unlimited messaging }, AccessGroupLimitMap: [{ AccessGroupOwnerPublicKeyBase58Check: "", ScopeType: "Any", AccessGroupKeyName: "", OperationType: "Any", OpCount: "UNLIMITED" }] } }); \`\`\` ### 2. Access Group Auto-Setup Pattern \`\`\`javascript // Auto-create default messaging access group const setupMessagingForUser = async (userPublicKey, messagingPublicKey) => { const { AccessGroupsOwned } = await getAllAccessGroups({ PublicKeyBase58Check: userPublicKey }); // Check if default messaging group exists const hasDefaultGroup = AccessGroupsOwned?.find( group => group.AccessGroupKeyName === "default-key" ); if (!hasDefaultGroup) { await createAccessGroup({ AccessGroupOwnerPublicKeyBase58Check: userPublicKey, AccessGroupPublicKeyBase58Check: messagingPublicKey, AccessGroupKeyName: "default-key", MinFeeRateNanosPerKB: 1000 }); } }; \`\`\` ### 3. Message Sending with Access Group Validation \`\`\`javascript const sendMessage = async (senderPublicKey, recipientPublicKey, messageText) => { // 1. Check party access groups const response = await checkPartyAccessGroups({ SenderPublicKeyBase58Check: senderPublicKey, SenderAccessGroupKeyName: "default-key", RecipientPublicKeyBase58Check: recipientPublicKey, RecipientAccessGroupKeyName: "default-key" }); // 2. Handle empty string responses (not undefined!) const senderKeyName = response.SenderAccessGroupKeyName || "default-key"; const recipientKeyName = response.RecipientAccessGroupKeyName || "default-key"; // 3. Encrypt message let encryptedMessage; let isUnencrypted = false; const ExtraData = {}; if (response.RecipientAccessGroupPublicKeyBase58Check) { encryptedMessage = await identity.encryptMessage( response.RecipientAccessGroupPublicKeyBase58Check, messageText ); } else { // Fallback to unencrypted (hex-encoded) encryptedMessage = bytesToHex(new TextEncoder().encode(messageText)); isUnencrypted = true; ExtraData["unencrypted"] = "true"; } // 4. Send message const result = await sendDMMessage({ SenderAccessGroupOwnerPublicKeyBase58Check: senderPublicKey, SenderAccessGroupPublicKeyBase58Check: response.SenderAccessGroupPublicKeyBase58Check, SenderAccessGroupKeyName: senderKeyName, RecipientAccessGroupOwnerPublicKeyBase58Check: recipientPublicKey, RecipientAccessGroupPublicKeyBase58Check: isUnencrypted ? response.RecipientPublicKeyBase58Check : response.RecipientAccessGroupPublicKeyBase58Check, RecipientAccessGroupKeyName: recipientKeyName, EncryptedMessageText: encryptedMessage, ExtraData, MinFeeRateNanosPerKB: 1000 }); return result.submittedTransactionResponse.TxnHashHex; }; \`\`\` ### 4. Message Retrieval and Decryption Pattern \`\`\`javascript const getConversationsWithDecryption = async (userPublicKey) => { // 1. Get all message threads const threads = await getAllMessageThreads({ UserPublicKeyBase58Check: userPublicKey }); // 2. Get access groups for decryption const { AccessGroupsOwned, AccessGroupsMember } = await getAllAccessGroups({ PublicKeyBase58Check: userPublicKey }); const allAccessGroups = (AccessGroupsOwned || []).concat(AccessGroupsMember || []); // 3. Decrypt messages with retry logic const { decrypted } = await decryptAccessGroupMessagesWithRetry( userPublicKey, threads.MessageThreads, allAccessGroups ); // 4. Process decrypted messages const conversations = {}; for (const decryptedMessage of decrypted) { if (!decryptedMessage.MessageInfo) continue; const otherUserKey = decryptedMessage.IsSender ? decryptedMessage.RecipientInfo.OwnerPublicKeyBase58Check : decryptedMessage.SenderInfo.OwnerPublicKeyBase58Check; // ✅ CRITICAL: Use DecryptedMessage property, not MessageText! let messageText = '[Encrypted Message]'; if (decryptedMessage.error) { messageText = '[Decryption Failed]'; } else if (decryptedMessage.DecryptedMessage) { messageText = decryptedMessage.DecryptedMessage; } else if (decryptedMessage.MessageInfo.ExtraData?.unencrypted === "true") { // Handle unencrypted messages try { const hexString = decryptedMessage.MessageInfo.EncryptedText; const bytes = new Uint8Array(Buffer.from(hexString, 'hex')); messageText = new TextDecoder().decode(bytes); } catch (error) { messageText = '[Decoding Failed]'; } } if (!conversations[otherUserKey]) { conversations[otherUserKey] = { messages: [], ChatType: decryptedMessage.ChatType, lastMessage: messageText, lastTimestamp: decryptedMessage.MessageInfo.TimestampNanos }; } conversations[otherUserKey].messages.push({ id: \`\${decryptedMessage.MessageInfo.TimestampNanos}-\${decryptedMessage.SenderInfo.OwnerPublicKeyBase58Check}\`, messageText, timestampNanos: decryptedMessage.MessageInfo.TimestampNanos, isFromSender: decryptedMessage.IsSender, isEncrypted: !decryptedMessage.MessageInfo.ExtraData?.unencrypted }); } return { conversations, profiles: threads.PublicKeyToProfileEntryResponse }; }; \`\`\` ### 5. Real-time Polling Pattern \`\`\`javascript const useMessagePolling = (userPublicKey, interval = 5000) => { const [conversations, setConversations] = useState({}); const [isPolling, setIsPolling] = useState(false); useEffect(() => { if (!userPublicKey) return; const pollMessages = async () => { try { setIsPolling(true); const { conversations: newConversations } = await getConversationsWithDecryption(userPublicKey); setConversations(newConversations); } catch (error) { console.error('Polling error:', error); } finally { setIsPolling(false); } }; // Initial load pollMessages(); // Set up polling const pollInterval = setInterval(pollMessages, interval); return () => clearInterval(pollInterval); }, [userPublicKey, interval]); return { conversations, isPolling }; }; \`\`\`` }, "error-handling": { title: "🛡️ Comprehensive Error Handling Pattern", content: `# DeSo Error Handling Best Practices ## 🎯 Categorized Error Handling ### 1. API Error Classification \`\`\`javascript const classifyError = (error) => { const message = error.message.toLowerCase(); if (message.includes('404') || message.includes('not found')) { return { type: 'NOT_FOUND', userMessage: 'User or resource not found', technical: error.message, recoverable: false }; } if (message.includes('network') || message.includes('fetch')) { return { type: 'NETWORK', userMessage: 'Network connection issue. Please try again.', technical: error.message, recoverable: true }; } if (message.includes('incorrect mac')) { return { type: 'DECRYPTION', userMessage: 'Message could not be decrypted', technical: error.message, recoverable: false }; } if (message.includes('access group')) { return { type: 'ACCESS_GROUP', userMessage: 'Messaging permissions issue', technical: error.message, recoverable: true }; } return { type: 'UNKNOWN', userMessage: 'An unexpected error occurred', technical: error.message, recoverable: false }; }; \`\`\` ### 2. Retry Logic with Exponential Backoff \`\`\`javascript const withRetry = async (apiCall, maxRetries = 3, baseDelay = 1000) => { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await apiCall(); } catch (error) { const errorInfo = classifyError(error); if (!errorInfo.recoverable || attempt === maxRetries) { throw error; } const delay = baseDelay * Math.pow(2, attempt - 1); console.warn(\`Attempt \${attempt} failed, retrying in \${delay}ms...\`); await new Promise(resolve => setTimeout(resolve, delay)); } } }; \`\`\` ### 3. React Error Boundary for DeSo Operations \`\`\`javascript class DeSoErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null, errorInfo: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { const classified = classifyError(error); console.error('DeSo Error Boundary:', { error, errorInfo, classified }); this.setState({ errorInfo: classified }); } render() { if (this.state.hasError) { return ( <div className="error-boundary"> <h2>DeSo Integration Error</h2> <p>{this.state.errorInfo?.userMessage}</p> {process.env.NODE_ENV === 'development' && ( <details> <summary>Technical Details</summary> <pre>{this.state.errorInfo?.technical}</pre> </details> )} <button onClick={() => this.setState({ hasError: false, error: null })}> Try Again </button> </div> ); } return this.props.children; } } \`\`\` ### 4. Graceful Degradation Pattern \`\`\`javascript const useDesoWithFallback = (apiCall, fallbackValue = null) => { const [data, setData] = useState(fallbackValue); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const execute = useCallback(async (...args) => { setLoading(true); setError(null); try { const result = await withRetry(() => apiCall(...args)); setData(result); return result; } catch (err) { const errorInfo = classifyError(err); setError(errorInfo); // Return fallback for non-critical errors if (errorInfo.type === 'NOT_FOUND') { setData(fallbackValue); return fallbackValue; } throw err; } finally { setLoading(false); } }, [apiCall, fallbackValue]); return { data, error, loading, execute }; }; \`\`\`` }, "state-management": { title: "🔄 DeSo State Management Pattern", content: `# DeSo State Management Best Practices ## 🎯 Centralized DeSo State with Context ### 1. DeSo Context Provider \`\`\`javascript const DeSoContext = createContext(); export const DeSoProvider = ({ children }) => { const [user, setUser] = useState({ isAuthenticated: false, publicKey: null, username: null, alternateUsers: [] }); const [messaging, setMessaging] = useState({ conversations: {}, messages: {}, loading: false, error: null }); const [sdk, setSdk] = useState({ isInitialized: false, functions: {} }); // Initialize SDK useEffect(() => { const initializeSdk = async () => { try { const desoFunctions = await import('deso-protocol'); setSdk({ isInitialized: true, functions: desoFunctions }); } catch (error) { console.error('Failed to initialize DeSo SDK:', error); } }; initializeSdk(); }, []); // Identity subscription useEffect(() => { if (!sdk.isInitialized) return; const { identity } = sdk.functions; const unsubscribe = identity.subscribe(({ currentUser, alternateUsers, event }) => { if (event === 'LOGIN_END' && currentUser) { setUser({ isAuthenticated: true, publicKey: currentUser.publicKey, username: currentUser.username || currentUser.publicKey?.slice(0, 10) + '...', alternateUsers: alternateUsers || [] }); } else if (event === 'LOGOUT') { setUser({ isAuthenticated: false, publicKey: null, username: null, alternateUsers: [] }); setMessaging({ conversations: {}, messages: {}, loading: false, error: null }); } }); return unsubscribe; }, [sdk.isInitialized]); const value = { user, messaging, sdk, setUser, setMessaging }; return ( <DeSoContext.Provider value={value}> {children} </DeSoContext.Provider> ); }; export const useDeSo = () => { const context = useContext(DeSoContext); if (!context) { throw new Error('useDeSo must be used within DeSoProvider'); } return context; }; \`\`\` ### 2. Custom Hooks for DeSo Operations \`\`\`javascript // Custom hook for messaging operations export const useDesoMessaging = () => { const { user, messaging, setMessaging, sdk } = useDeSo(); const sendMessage = useCallback(async (recipientPublicKey, messageText) => { if (!user.isAuthenticated || !sdk.isInitialized) { throw new Error('User not authenticated or SDK not initialized'); } setMessaging(prev => ({ ...prev, loading: true, error: null })); try { const { sendDMMessage, checkPartyAccessGroups, identity } = sdk.functions; // Implementation from messaging-flow pattern... const txnHash = await sendMessage(user.publicKey, recipientPublicKey, messageText); // Update local state optimistically setMessaging(prev => ({ ...prev, loading: false, // Add optimistic message to state })); return txnHash; } catch (error) { setMessaging(prev => ({ ...prev, loading: false, error: classifyError(error) })); throw error; } }, [user, sdk, setMessaging]); const refreshConversations = useCallback(async () => { if (!user.isAuthenticated) return; setMessaging(prev => ({ ...prev, loading: true })); try { const { conversations } = await getConversationsWithDecryption(user.publicKey); setMessaging(prev => ({ ...prev, conversations, loading: false, error: null })); } catch (error) { setMessaging(prev => ({ ...prev, loading: false, error: classifyError(error) })); } }, [user.publicKey, setMessaging]); return { conversations: messaging.conversations, loading: messaging.loading, error: messaging.error, sendMessage, refreshConversations }; }; \`\`\` ### 3. Optimistic Updates Pattern \`\`\`javascript const useOptimisticMessages = () => { const { messaging, setMessaging } = useDeSo(); const addOptimisticMessage = useCallback((conversationId, message) => { const optimisticMessage = { ...message, id: \`optimistic-\${Date.now()}\`, isOptimistic: true, timestamp: Date.now() }; setMessaging(prev => ({ ...prev, conversations: { ...prev.conversations, [conversationId]: { ...prev.conversations[conversationId], messages: [ optimisticMessage, ...(prev.conversations[conversationId]?.messages || []) ] } } })); return optimisticMessage.id; }, [setMessaging]); const confirmOptimisticMessage = useCallback((conversationId, optimisticId, confirmedMessage) => { setMessaging(prev => ({ ...prev, conversations: { ...prev.conversations, [conversationId]: { ...prev.conversations[conversationId], messages: prev.conversations[conversationId]?.messages.map(msg => msg.id === optimisticId ? { ...confirmedMessage, isOptimistic: false } : msg ) || [] } } })); }, [setMessaging]); return { addOptimisticMessage, confirmOptimisticMessage }; }; \`\`\`` } }; if (pattern === 'all') { let response = `# 🏗️ Complete DeSo Implementation Patterns Based on deso-chat analysis and real debugging experience. ## 🎯 Pattern Overview | Pattern | Purpose | Key Benefits | |---------|---------|--------------| | Messaging Flow | Complete message send/receive | Proper encryption, access groups | | Error Handling | Robust error management | User-friendly errors, retry logic | | State Management | Centralized DeSo state | Clean architecture, reusability | | API Integration | Consistent API usage | Proper response handling | | User Switching | Multi-account support | Professional UX | | Real-time Updates | Live message polling | Current conversation state | `; for (const [key, patternInfo] of Object.entries(patterns)) { response += `\n## ${patternInfo.title}\n\n`; response += patternInfo.content.split('\n').slice(2, 8).join('\n'); // Summary response += `\n\n*Use \`deso_implementation_patterns\` with pattern="${key}" for complete implementation*\n\n`; } return { content: [{ type: "text", text: response }] }; } if (patterns[pattern]) { return { content: [{ type: "text", text: patterns[pattern].content }] }; } return { content: [{ type: "text", text: `Unknown pattern: ${pattern}. Available: ${Object.keys(patterns).join(', ')}` }] }; } // Comprehensive DeSo UI Components tool based on deso-ui library analysis async function desoUIComponents(args) { const { action, component, category, framework = 'react', query } = args; // Complete component registry from deso-ui/registry.json analysis const componentRegistry = { social: { description: "Components for social interactions, posts, and feeds", components: { "post-card": { title: "Post Card", description: "A comprehensive card for displaying social media posts with images, videos, polls, quotes, and engagement stats", dependencies: ["user-info", "post-text", "post-image", "post-video", "post-audio", "post-embed", "post-poll", "post-engagement", "post-reactions", "action-menu", "timestamp"], props: ["publicKey", "postContent", "timestamp", "images", "embedUrl", "quotedPost", "status", "videoUrl", "audioUrl", "reactions", "comments", "poll", "layout", "nft"], examples: ["Default post", "With images", "With video", "With poll", "Quote post", "Repost", "NFT post"] }, "post-text": { title: "Post Text", description: "Component for displaying post text content with linkification and mentions", dependencies: [], props: ["content", "lineClamp", "variant"], examples: ["Simple text", "Rich text with links", "Truncated text"] }, "post-image": { title: "Post Image", description: "Advanced image display with bento grid layouts, carousels, and blur hash support", dependencies: [], props: ["images", "layout", "blurhash", "onImageClick"], examples: ["Single image", "Bento grid (2-4 images)", "Carousel (5+ images)"] }, "post-video": { title: "Post Video", description: "Video player component with react-player integration", dependencies: ["react-player"], props: ["videoUrl", "controls", "autoplay"], examples: ["YouTube embed", "Direct video file", "Vimeo embed"] }, "post-audio": { title: "Post Audio", description: "Audio player with waveform visualization using wavesurfer.js", dependencies: ["wavesurfer.js"], props: ["audioUrl", "title", "artist"], examples: ["Music track", "Podcast episode", "Voice note"] }, "post-embed": { title: "Post Embed", description: "Universal embed component for YouTube, Spotify, Twitter, and other platforms", dependencies: [], props: ["embedUrl", "platform"], examples: ["YouTube video", "Spotify track", "Twitter tweet"] }, "post-poll": { title: "Post Poll", description: "Interactive poll component with voting functionality", dependencies: [], props: ["options", "votes", "totalVotes", "userVotedIndex", "onVote"], examples: ["Multiple choice poll", "Binary poll", "Completed poll"] }, "post-engagement": { title: "Post Engagement", description: "Display and interact with post metrics (likes, comments, reposts, diamonds)", dependencies: [], props: ["comments", "likes", "reposts", "diamonds", "views", "onLike", "onComment", "onRepost"], examples: ["Basic engagement", "With actions", "High numbers formatting"] }, "post-reactions": { title: "Post Reactions", description: "Emoji reactions system for posts", dependencies: [], props: ["reactions", "onReactionClick"], examples: ["Reaction picker", "Reaction display", "Custom reactions"] }, "feed-list": { title: "Feed List", description: "Scrollable list of posts for news feeds", dependencies: ["post-card"], props: ["posts", "loading", "onLoadMore"], examples: ["Home feed", "Profile posts", "Infinite scroll"] } } }, user: { description: "Components for user profiles, authentication, and user interactions", components: { "profile-card": { title: "Profile Card", description: "Complete user profile card with stats, follow button, and actions", dependencies: ["profile-picture", "username-display", "profile-description", "profile-stat", "follow-button"], props: ["publicKey", "variant", "showStats", "showActions"], examples: ["Compact card", "Full profile", "Followers list item"] }, "profile-picture": { title: "Profile Picture", description: "Smart profile picture with NFT support, hex decoding, and fallbacks", dependencies: [], props: ["publicKey", "size", "shape", "nftImage"], examples: ["Standard avatar", "NFT profile pic", "Different sizes"] }, "username-display": { title: "Username Display", description: "Username with verification badge, truncation, and copy functionality", dependencies: ["verification-badge", "copy-button"], props: ["publicKey", "username", "variant", "truncate", "maxLength", "showCopy"], examples: ["Basic username", "Verified user", "Truncated display"] }, "user-info": { title: "User Info", description: "Combined user display with avatar, username, and timestamp", dependencies: ["profile-picture", "username-display", "timestamp"], props: ["publicKey", "timestamp", "layout"], examples: ["Post header", "Comment author", "Compact info"] }, "user-menu": { title: "User Menu", description: "Dropdown menu for authenticated user with account switching", dependencies: ["profile-picture", "username-display"], props: ["currentUser", "alternateUsers", "onSwitch", "onLogout"], examples: ["Single account", "Multi-account switcher", "Settings menu"] }, "user-search": { title: "User Search", description: "Typeahead search for finding users with profile previews", dependencies: ["search-bar", "user-info"], props: ["onUserSelect", "placeholder", "maxResults"], examples: ["Mention search", "Follow search", "Message recipient"] }, "follow-button": { title: "Follow Button", description: "Smart follow/unfollow button with state management", dependencies: [], props: ["targetPublicKey", "isFollowing", "variant", "onFollow"], examples: ["Follow button", "Icon only", "Disabled state"] }, "profile-list": { title: "Profile List", description: "List of user profiles for followers, following, etc.", dependencies: ["profile-card"], props: ["profiles", "variant", "showActions"], examples: ["Followers list", "Following list", "Search results"] } } }, messaging: { description: "Components for DeSo's encrypted messaging system", components: { "message-inbox": { title: "Message Inbox", description: "Complete chat interface with conversation list and message thread", dependencies: ["message-inbox-item", "message-chat-list", "editor"], props: ["conversations", "selectedConversation", "currentUser"], examples: ["Desktop inbox", "Mobile chat", "Empty state"] }, "message-chat-list": { title: "Message Chat List", description: "List of messages in a conversation thread", dependencies: ["message-chat-item"], props: ["messages", "currentUser", "onLoadMore"], examples: ["Active conversation", "Message history", "Loading states"] }, "message-chat-item": { title: "Message Chat Item", description: "Individual message bubble with timestamp and status", dependencies: ["profile-picture", "timestamp"], props: ["message", "isFromCurrentUser", "showTimestamp"], examples: ["Text message", "Media message", "System message"] }, "message-inbox-item": { title: "Message Inbox Item", description: "Conversation preview in the sidebar", dependencies: ["profile-picture", "timestamp"], props: ["conversation", "isSelected", "unreadCount"], examples: ["Recent conversation", "Unread messages", "Selected state"] }, "message-button": { title: "Message Button", description: "Button to initiate messaging with a user", dependencies: [], props: ["targetPublicKey", "variant", "onClick"], examples: ["Profile message button", "Icon only", "Disabled state"] } } }, media: { description: "Components for media display and interaction", components: { "media-gallery": { title: "Media Gallery", description: "Masonry layout gallery for images and videos", dependencies: ["media-item", "react-photo-album"], props: ["media", "layout", "onItemClick"], examples: ["Image gallery", "Mixed media", "Lightbox integration"] }, "media-card": { title: "Media Card", description: "Media preview card with hover effects and engagement", dependencies: ["post-engagement", "react-player"], props: ["mediaUrl", "mediaType", "engagement", "onPlay"], examples: ["Image card", "Video card", "Hover preview"] }, "media-item": { title: "Media Item", description: "Individual media item with optimization", dependencies: ["post-engagement"], props: ["mediaUrl", "mediaType", "size", "onClick"], examples: ["Thumbnail", "Full size", "Loading placeholder"] }, "video-reel": { title: "Video Reel", description: "TikTok-style vertical video player for short content", dependencies: ["user-info", "post-engagement", "post-text", "react-player"], props: ["videos", "currentIndex", "onVideoChange"], examples: ["Short video feed", "Single video", "Auto-play reel"] } } }, interactive: { description: "Interactive components for content creation and actions", components: { "editor": { title: "Editor", description: "Rich text editor with media uploads, emojis, and markdown support", dependencies: ["editor-emoji-picker", "editor-markdown", "editor-upload"], props: ["currentUser", "onSubmit", "placeholder", "maxChars", "showImageUpload", "showVideoUpload"], examples: ["Basic post editor", "Reply editor", "DM composer"] }, "editor-upload": { title: "Editor Upload", description: "File upload component with drag-and-drop and progress", dependencies: [], props: ["acceptedTypes", "maxFiles", "onUpload", "uploadType"], examples: ["Image upload", "Video upload", "Multiple files"] }, "editor-emoji-picker": { title: "Editor Emoji Picker", description: "Emoji picker for editor integration", dependencies: ["emoji-picker-react"], props: ["onEmojiClick", "theme"], examples: ["Emoji picker", "Custom categories", "Skin tone selector"] }, "search-bar": { title: "Search Bar", description: "Universal search with autocomplete and keyboard navigation", dependencies: [], props: ["placeholder", "onSearch", "suggestions", "loading"], examples: ["Global search", "User search", "Post search"] }, "action-menu": { title: "Action Menu", description: "Dropdown menu for post and user actions", dependencies: ["confirmation-dialog"], props: ["actions", "onAction", "triggerIcon"], examples: ["Post actions", "User actions", "Custom actions"] }, "confirmation-dialog": { title: "Confirmation Dialog", description: "Modal dialog for confirming destructive actions", dependencies: [], props: ["title", "description", "onConfirm", "onCancel"], examples: ["Delete confirmation", "Unfollow confirmation", "Block user"] } } }, navigation: { description: "Navigation and layout components", components: { "navigation-list": { title: "Navigation List", description: "Sidebar navigation with icons and badges", dependencies: [], props: ["items", "activeItem", "onItemClick"], examples: ["Main sidebar", "Mobile menu", "Settings nav"] }, "logo": { title: "Logo", description: "DeSo logo with light/dark mode support", dependencies: [], props: ["variant", "size", "theme"], examples: ["Header logo", "Loading logo", "Favicon"] } } }, utility: { description: "Utility components for common functionality", components: { "timestamp": { title: "Timestamp", description: "Smart timestamp display with relative and absolute formatting", dependencies: ["date-fns"], props: ["date", "format", "relative"], examples: ["Relative time", "Full date", "Custom format"] }, "copy-button": { title: "Copy Button", description: "Button with copy-to-clipboard functionality", dependencies: [], props: ["text", "variant", "showFeedback"], examples: ["Copy text", "Copy link", "Copy public key"] }, "verification-badge": { title: "Verification Badge", description: "Verification badge with multiple styles", dependencies: [], props: ["verified", "variant", "size"], examples: ["Verified user", "Different styles", "Custom colors"] } } } }; const exampleLayouts = { "twitter-clone": { title: "Twitter-like Social Media", description: "Complete social media layout with feeds, profiles, and messaging", components: ["navigation-list", "user-menu", "feed-list", "post-card", "editor", "search-bar", "profile-card"], layout: "Three-column layout with sidebar navigation, main feed, and right sidebar" }, "messaging-app": { title: "DeSo Messaging Application", description: "Encrypted messaging interface with conversation management", components: ["message-inbox", "message-chat-list", "message-chat-item", "user-search", "editor"], layout: "Two-panel layout with conversation list and active chat" }, "profile-page": { title: "User Profile Page", description: "Complete user profile with posts, followers, and actions", components: ["profile-cover-photo", "profile-picture", "username-display", "profile-description", "profile-stat", "follow-button", "feed-list"], layout: "Profile header with tabbed content sections" }, "media-gallery": { title: "Media Gallery Application", description: "Instagram-like media browsing and discovery", components: ["media-gallery", "media-card", "video-reel", "user-info", "post-engagement"], layout: "Grid layout with modal overlays for detailed view" } }; switch (action) { case 'explore': if (component) { return exploreSpecificComponent(component, componentRegistry, framework); } else if (category && category !== 'all') { return exploreCategoryComponents(category, componentRegistry, framework); } else { return exploreAllComponents(componentRegistry); } case 'install': return generateInstallInstructions(component, componentRegistry); case 'usage': return generateUsageExamples(component, componentRegistry, framework); case 'dependencies': return showComponentDependencies(component, componentRegistry); case 'examples': return showComponentExamples(component, componentRegistry, framework); case 'layouts': return showExampleLayouts(exampleLayouts, framework); case 'categories': return showComponentCategories(componentRegistry); case 'search': return searchComponents(query, componentRegistry); default: return { content: [{ type: "text", text: `Unknown action: ${action}. Available: explore, install, usage, dependencies, examples, layouts, categories, search` }] }; } } // Helper functions for DeSo UI Components function exploreAllComponents(registry) { let response = `# 🎨 DeSo UI Component Library A comprehensive React component library for building DeSo blockchain applications. **Built with:** TypeScript, Shadcn UI, Tailwind CSS, Storybook **Installation:** \`npx shadcn@latest add http://ui.deso.com/r/[component].json\` ## 📊 Component Overview `; let totalComponents = 0; for (const [categoryName, categoryInfo] of Object.entries(registry)) { const componentCount = Object.keys(categoryInfo.components).length; totalComponents += componentCount; response += `### ${categoryName.toUpperCase()} (${componentCount} components)\n`; response += `${categoryInfo.description}\n\n`; for (const [componentName, componentInfo] of Object.entries(categoryInfo.components)) { response += `- **${componentInfo.title}**: ${componentInfo.description}\n`; } response += `\n`; } response += `## 🚀 Quick Start \`\`\`bash # Install a specific component npx shadcn@latest add http://ui.deso.com/r/post-card.json # Install all shadcn base components npx shadcn@latest add --all \`\`\` **Total Components:** ${totalComponents} Use \`deso_ui_components\` with specific actions: - \`explore\` - Explore categories or specific components - \`install\` - Get installation commands - \`usage\` - See usage examples - \`examples\` - View component examples - \`layouts\` - See complete layout examples`; return { content: [{ type: "text", text: response }] }; } function exploreSpecificComponent(componentName, registry, framework) { // Find component across all categories let foundComponent = null; let foundCategory = null; for (const [categoryName, categoryInfo] of Object.entries(registry)) { if (categoryInfo.components[componentName]) { foundComponent = categoryInfo.components[componentName]; foundCategory = categoryName; break; } } if (!foundComponent) { return { content: [{ type: "text", text: `Component "${componentName}" not found. Use 'search' action to find similar components.` }] }; } let response = `# ${foundComponent.title} **Category:** ${foundCategory.toUpperCase()} **Component:** \`${componentName}\` ${foundComponent.description} ## 🛠️ Installation \`\`\`bash npx shadcn@latest add http://ui.deso.com/r/${componentName}.json \`\`\` ## 📋 Dependencies `; if (foundComponent.dependencies && foundComponent.dependencies.length > 0) { response += `**Registry Dependencies:**\n`; for (const dep of foundComponent.dependencies) { response += `- ${dep}\n`; } } else { response += `No additional dependencies required.\n`; } response += `\n## 🎯 Props **Key Props:**\n`; for (const prop of foundComponent.props || []) { response += `- \`${prop}\`\n`; } response += `\n## 📱 Usage Example (${framework}) \`\`\`${framework === 'react' ? 'tsx' : 'javascript'} import { ${foundComponent.title.replace(/\s+/g, '')} } from '@/components/deso-ui/${componentName}'; function MyComponent() { return ( <${foundComponent.title.replace(/\s+/g, '')} // Add your props here /> ); } \`\`\` ## ✨ Examples `; for (const example of foundComponent.examples || []) { response += `- ${example}\n`; } return { content: [{ type: "text", text: response }] }; } function generateInstallInstructions(componentName, registry) { if (!componentName) { return { content: [{ type: "text", text: "Please specify a component name for installation instructions." }] }; } let response = `# 📦 Installation Instructions ## Install ${componentName} \`\`\`bash npx shadcn@latest add http://ui.deso.com/r/${componentName}.json \`\`\` ## Setup Dependencies \`\`\`bash # Install required npm packages if needed npm install deso-protocol lucide-react class-variance-authority \`\`\` ## Usage \`\`\`tsx import { ${componentName.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('')} } from '@/components/deso-ui/${componentName}'; export default function MyApp() { return ( <${componentName.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('')} /> ); } \`\`\` ## Additional Setup For DeSo integration, configure the SDK: \`\`\`tsx import { configure } from 'deso-protocol'; configure({ nodeURI: 'https://node.deso.org', identityURI: 'https://identity.deso.org' }); \`\`\``; return { content: [{ type: "text", text: response }] }; } function showExampleLayouts(layouts, framework) { let response = `# 🏗️ Complete Layout Examples Ready-to-use layout patterns combining multiple DeSo UI components. `; for (const [layoutName, layoutInfo] of Object.entries(layouts)) { response += `## ${layoutInfo.title} ${layoutInfo.description} **Layout:** ${layoutInfo.layout} **Components Used:** `; for (const component of layoutInfo.components) { response += `- ${component}\n`; } response += `\n**Installation:**\n\`\`\`bash\n`; for (const component of layoutInfo.components) { response += `npx shadcn@latest add http://ui.deso.com/r/${component}.json\n`; } response += `\`\`\`\n\n`; } response += `## 🎯 Framework Integration ### Next.js Setup \`\`\`bash npx create-next-app@latest my-deso-app --typescript --tailwind --app cd my-deso-app npm install deso-protocol \`\`\` ### Component Installation \`\`\`bash # Install all base UI components npx shadcn@latest add --all # Install specific DeSo components npx shadcn@latest add http://ui.deso.com/r/post-card.json \`\`\``; return { content: [{ type: "text", text: response }] }; } function exploreCategoryComponents(category, registry, framework) { if (!registry[category]) { return { content: [{ type: "text", text: `Category "${category}" not found. Available: ${Object.keys(registry).join(', ')}` }] }; } const categoryInfo = registry[category]; let response = `# ${category.toUpperCase()} Components ${categoryInfo.description} ## Components (${Object.keys(categoryInfo.components).length}) `; for (const [componentName, componentInfo] of Object.entries(categoryInfo.components)) { response += `### ${componentInfo.title} (\`${componentName}\`) ${componentInfo.description} **Installation:** \`npx shadcn@latest add http://ui.deso.com/r/${componentName}.json\` **Key Props:** ${componentInfo.props?.join(', ') || 'None'} **Dependencies:** ${componentInfo.dependencies?.length > 0 ? componentInfo.dependencies.join(', ') : 'None'} --- `; } return { content: [{ type: "text", text: response }] }; } function generateUsageExamples(componentName, registry, framework) { if (!componentName) { return { content: [{ type: "text", text: "Please specify a component name for usage examples." }] }; } let foundComponent = null; for (const [categoryName, categoryInfo] of Object.entries(registry)) { if (categoryInfo.components[componentName]) { foundComponent = categoryInfo.components[componentName]; break; } } if (!foundComponent) { return { content: [{ type: "text", text: `Component "${componentName}" not found.` }] }; } const componentPascal = componentName.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(''); let response = `# ${foundComponent.title} Usage Examples ## Basic Usage \`\`\`${framework === 'react' ? 'tsx' : 'javascript'} import { ${componentPascal} } from '@/components/deso-ui/${componentName}'; export default function Example() { return ( <${componentPascal} // Basic props /> ); } \`\`\` ## Advanced Examples `; // Generate specific examples based on component type if (componentName === 'post-card') { response += `### Social Media Post \`\`\`tsx <PostCard publicKey="BC1YLi..." postContent="Hello DeSo! 🚀" timestamp={new Date()} images={["https://example.com/image.jpg"]} actions={{ comments: 5, likes: 25, reposts: 3, diamonds: 2, views: 150 }} /> \`\`\` ### Post with Poll \`\`\`tsx <PostCard publicKey="BC1YLi..." postContent="What's your favorite DeSo feature?" poll={{ options: [ { text: "Creator Coins" }, { text: "Diamonds" }, { text: "NFTs" } ], votes: [15, 10, 8], totalVotes: 33, userVotedIndex: null }} /> \`\`\``; } else if (componentName === 'editor') { response += `### Post Composer \`\`\`tsx <Editor currentUser={{ publicKey: "BC1YLi...", profile: { username: "creator" } }} onSubmit={(data) => { console.log('Post submitted:', data); }} placeholder="What's on your mind?" maxChars={600} showImageUpload={true} showVideoUpload={true} /> \`\`\``; } else if (componentName === 'message-inbox') { response += `### Chat Interface \`\`\`tsx <MessageInbox conversations={[ { id: "conv1", otherUser: { publicKey: "BC1YLi...", username: "friend" }, lastMessage: "Hey there!", unreadCount: 2 } ]} selectedConversation="conv1" currentUser={{ publicKey: "BC1YLi..." }} /> \`\`\``; } response += `\n## Integration with DeSo SDK \`\`\`tsx import { identity, ${componentName.includes('post') ? 'submitPost' : 'getSingleProfile'} } from 'deso-protocol'; import { ${componentPascal} } from '@/components/deso-ui/${componentName}'; function DeSoIntegratedExample() { const [user, setUser] = useState(null); useEffect(() => { identity.subscribe((state) => { setUser(state.currentUser); }); }, []); return user ? ( <${componentPascal} publicKey={user.publicKey} // Additional DeSo-specific props /> ) : ( <div>Please log in</div> ); } \`\`\``; return { content: [{ type: "text", text: response }] }; } function showComponentDependencies(componentName, registry) { if (!componentName) { return { content: [{ type: "text", text: "Please specify a component name to show dependencies." }] }; } let foundComponent = null; let foundCategory = null; for (const [categoryName, categoryInfo] of Object.entries(registry)) { if (categoryInfo.components[componentName]) { foundComponent = categoryInfo.components[componentName]; foundCategory = categoryName; break; } } if (!foundComponent) { return { content: [{ type: "text", text: `Component "${componentName}" not found.` }] }; } let response = `# ${foundComponent.title} Dependencies ## Installation Order \`\`\`bash # Install base component first npx shadcn@latest add http://ui.deso.com/r/${componentName}.json `; if (foundComponent.dependencies && foundComponent.dependencies.length > 0) { response += `\n# Install dependencies\n`; for (const dep of foundComponent.dependencies) { if (dep.startsWith('react-') || dep.includes('.')) { response += `npm install ${dep}\n`; } else { response += `npx shadcn@latest add http://ui.deso.com/r/${dep}.json\n`; } } } response += `\`\`\` ## Dependency Tree \`\`\` ${componentName} `; if (foundComponent.dependencies && foundComponent.dependencies.length > 0) { for (const dep of foundComponent.dependencies) { response += `├── ${dep}\n`; } } else { response += `└── (no dependencies)\n`; } response += `\`\`\` ## Component Imports \`\`\`tsx // Main component import { ${foundComponent.title.replace(/\s+/g, '')} } from '@/components/deso-ui/${componentName}'; `; if (foundComponent.dependencies && foundComponent.dependencies.length > 0) { response += `// Dependencies (automatically imported)\n`; for (const dep of foundComponent.dependencies) { if (!dep.startsWith('react-') && !dep.includes('.')) { const depPascal = dep.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(''); response += `// import { ${depPascal} } from '@/components/deso-ui/${dep}';\n`; } } } response += `\`\`\``; return { content: [{ type: "text", text: response }] }; } function showComponentCategories(registry) { let response = `# 📁 Component Categories Overview of all component categories in the DeSo UI library. `; for (const [categoryName, categoryInfo] of Object.entries(registry)) { const componentCount = Object.keys(categoryInfo.components).length; response += `## ${categoryName.toUpperCase()} (${componentCount} components) ${categoryInfo.description} **Components:** `; for (const [componentName, componentInfo] of Object.entries(categoryInfo.components)) { response += `- \`${componentName}\` - ${componentInfo.title}\n`; } response += `\n**Install all ${categoryName} components:** \`\`\`bash `; for (const componentName of Object.keys(categoryInfo.components)) { response += `npx shadcn@latest add http://ui.deso.com/r/${componentName}.json\n`; } response += `\`\`\` --- `; } return { content: [{ type: "text", text: response }] }; } function searchComponents(query, registry) { if (!query) { return { content: [{ type: "text", text: "Please provide a search query." }] }; } const searchTerm = query.toLowerCase(); const results = []; for (const [categoryName, categoryInfo] of Object.entries(registry)) { for (const [componentName, componentInfo] of Object.entries(categoryInfo.components)) { const searchableText = `${componentName} ${componentInfo.title} ${componentInfo.description}`.toLowerCase(); if (searchableText.includes(searchTerm)) { results.push({ category: categoryName, name: componentName, title: componentInfo.title, description: componentInfo.description, score: (searchableText.match(new RegExp(searchTerm, 'g')) || []).length }); } } } // Sort by relevance results.sort((a, b) => b.score - a.score); if (results.length === 0) { return { content: [{ type: "text", text: `No components found matching "${query}".` }] }; } let response = `# 🔍 Search Results for "${query}" Found ${results.length} matching components: `; for (const result of results.slice(0, 10)) { // Show top 10 results response += `## ${result.title} (\`${result.name}\`) **Category:** ${result.category.toUpperCase()} **Description:** ${result.description} **Install:** \`npx shadcn@latest add http://ui.deso.com/r/${result.name}.json\` --- `; } if (results.length > 10) { response += `*Showing top 10 of ${results.length} results*`; } return { content: [{ type: "text", text: response }] }; } // Comprehensive DeSo GraphQL Helper based on schema analysis async function desoGraphQLHelper(args) { const { action, queryType, username, publicKey, question, customQuery } = args; switch (action) { case "query": return buildGraphQLQuery(queryType, username, publicKey, question); case "schema": return exploreGraphQLSchema(queryType); case "examples": return getGraphQLExamples(queryType); case "build": return buildQueryFromQuestion(question, username, publicKey); case "explain": return explainGraphQLQuery(customQuery); default: return { content: [{ type: "text", text: `Unknown GraphQL action: ${action}. Available: query, schema, examples, build, explain` }] }; } } function buildGraphQLQuery(queryType, username, publicKey, question) { const queries = { user: { title: "Get User Information", description: "Fetch user profile data including username, description, follower counts, and coin price", query: `query GetUser($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { id publicKey username description profilePic creatorBasisPoints coinPriceDesoNanos desoLockedNanos ccCoinsInCirculationNanos followers { totalCount } following { totalCount } posts { totalCount } } } }`, variables: username ? `{ "username": "${username}" }` : `{ "username": "YOUR_USERNAME_HERE" }`, example: username ? `// Example: Get user info for ${username}` : "// Example: Get user info for a specific username" }, followers: { title: "Get User Followers", description: "Fetch all followers of a specific user with their profile information", query: `query GetFollowers($username: String!, $first: Int = 10) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { username followers(first: $first, orderBy: [PRIMARY_KEY_DESC]) { totalCount nodes { follower { username publicKey description profilePic } } } } } }`, variables: username ? `{ "username": "${username}", "first": 10 }` : `{ "username": "YOUR_USERNAME_HERE", "first": 10 }`, example: username ? `// Example: Get followers for ${username}` : "// Example: Get followers for a specific user" }, following: { title: "Get Users Following", description: "Fetch all users that a specific user is following", query: `query GetFollowing($username: String!, $first: Int = 10) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { username following(first: $first, orderBy: [PRIMARY_KEY_DESC]) { totalCount nodes { followee { username publicKey description profilePic } } } } } }`, variables: username ? `{ "username": "${username}", "first": 10 }` : `{ "username": "YOUR_USERNAME_HERE", "first": 10 }`, example: username ? `// Example: Get users ${username} is following` : "// Example: Get users a specific user is following" }, posts: { title: "Get User Posts", description: "Fetch recent posts by a specific user with engagement metrics", query: `query GetUserPosts($username: String!, $first: Int = 10) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { username posts(first: $first, orderBy: [TIMESTAMP_DESC]) { totalCount nodes { postHash body imageUrls videoUrls timestamp isQuotedRepost isPinned likes { totalCount } diamonds { totalCount } parentPost { postHash body poster { username } } repostedPost { postHash body poster { username } } } } } } }`, variables: username ? `{ "username": "${username}", "first": 10 }` : `{ "username": "YOUR_USERNAME_HERE", "first": 10 }`, example: username ? `// Example: Get recent posts by ${username}` : "// Example: Get recent posts by a specific user" }, likes: { title: "Get User Likes", description: "Fetch posts that a user has liked", query: `query GetUserLikes($username: String!, $first: Int = 10) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { username likes(first: $first, orderBy: [PRIMARY_KEY_DESC]) { totalCount nodes { post { postHash body timestamp poster { username profilePic } likes { totalCount } } } } } } }`, variables: username ? `{ "username": "${username}", "first": 10 }` : `{ "username": "YOUR_USERNAME_HERE", "first": 10 }`, example: username ? `// Example: Get posts liked by ${username}` : "// Example: Get posts liked by a specific user" }, diamonds: { title: "Get User Diamonds", description: "Fetch diamonds sent and received by a user", query: `query GetUserDiamonds($username: String!, $first: Int = 10) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { username diamondsSent(first: $first, orderBy: [PRIMARY_KEY_DESC]) { totalCount nodes { diamondLevel post { postHash body poster { username } } receiver { username } } } diamondsReceived(first: $first, orderBy: [PRIMARY_KEY_DESC]) { totalCount nodes { diamondLevel post { postHash body } sender { username } } } } } }`, variables: username ? `{ "username": "${username}", "first": 10 }` : `{ "username": "YOUR_USERNAME_HERE", "first": 10 }`, example: username ? `// Example: Get diamonds for ${username}` : "// Example: Get diamonds for a specific user" }, nfts: { title: "Get User NFTs", description: "Fetch NFTs created or owned by a user", query: `query GetUserNFTs($username: String!, $first: Int = 10) { accounts(filter: { username: { equalToInsensitive: $username } }, first: 1) { nodes { username posts(filter: { isNft: { equalTo: true } }, first: $first, orderBy: [TIMESTAMP_DESC]) { totalCount nodes { postHash body imageUrls isNft numNftCopies numNftCopiesForSale numNftCopiesBurned nftRoyaltyToCreatorBasisPoints nfts(first: 5) { nodes { serialNumber isForSale minBidAmountNanos owner { username } } } nftBids(first: 5, orderBy: [BID_AMOUNT_NANOS_DESC]) { nodes { bidAmountNanos bidder { username } } } } } } } }`, variables: username ? `{ "username": "${username}", "first": 10 }` : `{ "username": "YOUR_USERNAME_HERE", "first": 10 }`, example: username ? `// Example: Get NFTs by ${username}` : "// Example: Get NFTs by a specific user" } }; if (!queryType || !queries[queryType]) { return { content: [{ type: "text", text: `Available query types: ${Object.keys(queries).join(', ')}` }] }; } const queryInfo = queries[queryType]; let response = `# ${queryInfo.title}\n\n${queryInfo.description}\n\n## GraphQL Query\n\n\`\`\`graphql\n${queryInfo.query}\n\`\`\`\n\n## Variables\n\n\`\`\`json\n${queryInfo.variables}\n\`\`\`\n\n## Usage Example\n\n\`\`\`javascript\n${queryInfo.example}\nconst response = await fetch('https://graphql.deso.com/graphql', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n query: \`${queryInfo.query.replace(/`/g, '\\`')}\`,\n variables: ${queryInfo.variables}\n })\n});\n\nconst data = await response.json();\nconsole.log(data);\n\`\`\``; // Add specific usage tips based on query type if (queryType === 'followers' || queryType === 'following') { response += `\n\n## Tips\n\n- Use \`totalCount\` to get the exact number of ${queryType}\n- Adjust \`first\` parameter to get more/fewer results\n- Use \`orderBy: [PRIMARY_KEY_DESC]\` for most recent ${queryType}`; } return { content: [{ type: "text", text: response }] }; } function exploreGraphQLSchema(queryType) { const schemas = { user: { title: "Account/User Schema", description: "The Account type represents a user profile on DeSo", fields: ` **Core Fields:** - \`id\`: Globally unique identifier - \`publicKey\`: User's public key (unique identifier) - \`username\`: Display username (can be null) - \`description\`: Profile bio/description - \`profilePic\`: Profile picture URL - \`creatorBasisPoints\`: Creator coin creator basis points - \`coinPriceDesoNanos\`: Current creator coin price in DeSo nanos - \`desoLockedNanos\`: Amount of DeSo locked in creator coin - \`ccCoinsInCirculationNanos\`: Creator coins in circulation **Relationships:** - \`followers\`: Users following this account - \`following\`: Users this account follows - \`posts\`: Posts created by this user - \`likes\`: Posts liked by this user - \`diamondsSent\`/\`diamondsReceived\`: Diamond transactions - \`creatorCoinBalances\`: Creator coin holdings`, filters: ` **Filtering Options:** - \`username: { equalToInsensitive: "username" }\` - Find by username (case-insensitive) - \`publicKey: { equalTo: "publickey" }\` - Find by public key - \`description: { includesInsensitive: "keyword" }\` - Search in bio - \`coinPriceDesoNanos: { greaterThan: "1000000000" }\` - Filter by coin price` }, posts: { title: "Post Schema", description: "The Post type represents a post/content on DeSo", fields: ` **Core Fields:** - \`postHash\`: Unique identifier for the post - \`posterPublicKey\`: Public key of the post creator - \`body\`: Text content of the post - \`imageUrls\`: Array of image URLs - \`videoUrls\`: Array of video URLs - \`timestamp\`: When the post was created - \`isQuotedRepost\`: Whether this is a quote repost - \`isPinned\`: Whether the post is pinned - \`isNft\`: Whether the post is an NFT **NFT Fields:** - \`numNftCopies\`: Total NFT copies - \`numNftCopiesForSale\`: Copies currently for sale - \`nftRoyaltyToCreatorBasisPoints\`: Creator royalty percentage **Relationships:** - \`poster\`: Account that created the post - \`parentPost\`: Post being replied to (for comments) - \`repostedPost\`: Post being reposted - \`likes\`: Users who liked this post - \`diamonds\`: Diamonds given to this post - \`nfts\`: NFT copies if this is an NFT post`, filters: ` **Filtering Options:** - \`posterPublicKey: { equalTo: "publickey" }\` - Posts by specific user - \`body: { includesInsensitive: "keyword" }\` - Search post content - \`isNft: { equalTo: true }\` - Only NFT posts - \`timestamp: { greaterThan: "2024-01-01" }\` - Posts after date - \`isPinned: { equalTo: true }\` - Only pinned posts` }, followers: { title: "Follow Schema", description: "The Follow type represents follower relationships", fields: ` **Core Fields:** - \`followerPkid\`: PKID of the user doing the following - \`followedPkid\`: PKID of the user being followed **Relationships:** - \`follower\`: Account doing the following - \`followee\`: Account being followed`, filters: ` **Filtering Options:** - \`followerPkid: { equalTo: "pkid" }\` - Find who a user follows - \`followedPkid: { equalTo: "pkid" }\` - Find who follows a user` } }; const schema = schemas[queryType] || schemas.user; let response = `# DeSo GraphQL Schema: ${schema.title}\n\n${schema.description}\n\n## Fields\n${schema.fields}\n\n## Filtering\n${schema.filters}`; response += `\n\n## Common Patterns\n\n**Pagination:**\n\`\`\`graphql\n{\n accounts(first: 10, after: "cursor") {\n nodes { ... }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n}\n\`\`\`\n\n**Ordering:**\n\`\`\`graphql\n{\n posts(orderBy: [TIMESTAMP_DESC]) {\n nodes { ... }\n }\n}\n\`\`\``; return { content: [{ type: "text", text: response }] }; } function getGraphQLExamples(queryType) { const examples = { user: [ { title: "Get User Follower Count", query: `query GetFollowerCount($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username followers { totalCount } } } }`, description: "Simple query to get just the follower count for a user" }, { title: "Get User Profile Summary", query: `query GetUserSummary($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username description profilePic followers { totalCount } following { totalCount } posts { totalCount } coinPriceDesoNanos } } }`, description: "Complete user profile summary with all key metrics" } ], posts: [ { title: "Get Recent Posts with Engagement", query: `query GetRecentPosts($first: Int = 10) { posts(first: $first, orderBy: [TIMESTAMP_DESC]) { nodes { postHash body timestamp poster { username profilePic } likes { totalCount } diamonds { totalCount } } } }`, description: "Get recent posts across the platform with engagement metrics" }, { title: "Get User's Last Post", query: `query GetLastPost($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { posts(first: 1, orderBy: [TIMESTAMP_DESC]) { nodes { body timestamp likes { totalCount } } } } } }`, description: "Get the most recent post by a specific user" } ] }; const typeExamples = examples[queryType] || examples.user; let response = `# GraphQL Examples: ${queryType}\n\n`; typeExamples.forEach((example, index) => { response += `## ${index + 1}. ${example.title}\n\n${example.description}\n\n\`\`\`graphql\n${example.query}\n\`\`\`\n\n`; }); response += `## Usage with JavaScript\n\n\`\`\`javascript\nconst query = \`...\`; // Your GraphQL query\nconst variables = { username: "nader" };\n\nconst response = await fetch('https://graphql.deso.com/graphql', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ query, variables })\n});\n\nconst data = await response.json();\nconsole.log(data.data);\n\`\`\``; return { content: [{ type: "text", text: response }] }; } function buildQueryFromQuestion(question, username, publicKey) { if (!question) { return { content: [{ type: "text", text: "Please provide a question to convert to GraphQL. Example: 'How many followers does nader have?'" }] }; } const questionLower = question.toLowerCase(); let queryType, queryTemplate, explanation; // Determine query type based on question if (questionLower.includes('follower') && questionLower.includes('how many')) { queryType = 'follower-count'; queryTemplate = `query GetFollowerCount($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username followers { totalCount } } } }`; explanation = "This query gets the total follower count for a user"; } else if (questionLower.includes('following') && questionLower.includes('how many')) { queryType = 'following-count'; queryTemplate = `query GetFollowingCount($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username following { totalCount } } } }`; explanation = "This query gets the total count of users being followed"; } else if (questionLower.includes('last post') || questionLower.includes('recent post')) { queryType = 'last-post'; queryTemplate = `query GetLastPost($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username posts(first: 1, orderBy: [TIMESTAMP_DESC]) { nodes { postHash body timestamp likes { totalCount } diamonds { totalCount } } } } } }`; explanation = "This query gets the most recent post by a user"; } else if (questionLower.includes('posts') && questionLower.includes('how many')) { queryType = 'post-count'; queryTemplate = `query GetPostCount($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username posts { totalCount } } } }`; explanation = "This query gets the total number of posts by a user"; } else if (questionLower.includes('profile') || questionLower.includes('info')) { queryType = 'profile'; queryTemplate = `query GetProfile($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username description profilePic followers { totalCount } following { totalCount } posts { totalCount } coinPriceDesoNanos } } }`; explanation = "This query gets complete profile information for a user"; } else { // Default to profile query queryType = 'general'; queryTemplate = `query GetUserInfo($username: String!) { accounts(filter: { username: { equalToInsensitive: $username } }) { nodes { username description followers { totalCount } following { totalCount } posts { totalCount } } } }`; explanation = "This is a general user information query based on your question"; } const targetUsername = username || (questionLower.match(/\b(\w+)\s+have/)?.[1]) || "YOUR_USERNAME_HERE"; const variables = `{ "username": "${targetUsername}" }`; let response = `# GraphQL Query for: "${question}"\n\n${explanation}\n\n## Generated Query\n\n\`\`\`graphql\n${queryTemplate}\n\`\`\`\n\n## Variables\n\n\`\`\`json\n${variables}\n\`\`\`\n\n## Complete Example\n\n\`\`\`javascript\nconst query = \`${queryTemplate.replace(/`/g, '\\`')}\`;\nconst variables = ${variables};\n\nconst response = await fetch('https://graphql.deso.com/graphql', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ query, variables })\n});\n\nconst data = await response.json();\nconsole.log(data.data.accounts.nodes[0]);\n\`\`\``; if (targetUsername === "YOUR_USERNAME_HERE") { response += `\n\n**Note:** Replace "YOUR_USERNAME_HERE" with the actual username you want to query.`; } return { content: [{ type: "text", text: response }] }; } function explainGraphQLQuery(customQuery) { if (!customQuery) { return { content: [{ type: "text", text: "Please provide a GraphQL query to explain." }] }; } let explanation = `# GraphQL Query Explanation\n\n## Your Query\n\n\`\`\`graphql\n${customQuery}\n\`\`\`\n\n## Analysis\n\n`; // Basic query analysis if (customQuery.includes('accounts')) { explanation += "- **accounts**: Querying user/account data from the DeSo blockchain\n"; } if (customQuery.includes('posts')) { explanation += "- **posts**: Querying post/content data\n"; } if (customQuery.includes('follows')) { explanation += "- **follows**: Querying follower relationship data\n"; } if (customQuery.includes('filter:')) { explanation += "- **filter**: Applying conditions to narrow down results\n"; } if (customQuery.includes('first:')) { explanation += "- **first**: Limiting the number of results (pagination)\n"; } if (customQuery.includes('orderBy:')) { explanation += "- **orderBy**: Sorting the results\n"; } if (customQuery.includes('totalCount')) { explanation += "- **totalCount**: Getting the total count without fetching all records\n"; } // Check for common patterns if (customQuery.includes('username') && customQuery.includes('equalToInsensitive')) { explanation += "\n## Pattern Detected: User Lookup\nThis query is looking up a user by username (case-insensitive).\n"; } if (customQuery.includes('followers') || customQuery.includes('following')) { explanation += "\n## Pattern Detected: Social Relationships\nThis query is examining follower/following relationships.\n"; } if (customQuery.includes('TIMESTAMP_DESC')) { explanation += "\n## Pattern Detected: Recent Content\nThis query is ordering by timestamp to get the most recent items first.\n"; } explanation += `\n## Tips for Optimization\n\n1. **Use pagination**: Always include \`first\` parameter to limit results\n2. **Request only needed fields**: Don't fetch unnecessary data\n3. **Use totalCount**: For counts, use \`totalCount\` instead of fetching all records\n4. **Case-insensitive search**: Use \`equalToInsensitive\` for username lookups\n5. **Proper ordering**: Use appropriate \`orderBy\` for your use case\n\n## Execution\n\nTo run this query:\n\n\`\`\`javascript\nconst response = await fetch('https://graphql.deso.com/graphql', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n query: \`${customQuery.replace(/`/g, '\\`')}\`,\n variables: {} // Add your variables here\n })\n});\n\nconst data = await response.json();\nconsole.log(data);\n\`\`\``; return { content: [{ type: "text", text: explanation }] }; } // Start server with robust error handling async function main() { try { // Create HTTP transport for MCP (stateless mode for simpler integration) const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, // Stateless mode enableJsonResponse: true // Enable JSON responses instead of SSE for simpler integration }); // Connect the MCP server to the transport await server.connect(transport); // Create HTTP server to handle the transport const httpServer = http.createServer(async (req, res) => { // Set CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Session-Id'); res.setHeader('Access-Control-Max-Age', '86400'); // Handle CORS preflight requests if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // Handle MCP requests if (req.method === 'POST' || req.method === 'GET' || req.method === 'DELETE') { let body = ''; if (req.method === 'POST') { req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { const parsedBody = body ? JSON.parse(body) : undefined; transport.handleRequest(req, res, parsedBody); }); } else { transport.handleRequest(req, res); } return; } // 404 for other routes res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found', message: 'This is a Model Context Protocol server. Send JSON-RPC requests to this endpoint.' })); }); // Start the HTTP server httpServer.listen(PORT, HOST, () => { console.error(`🚀 DeSo MCP Server v3.0 running on http://${HOST}:${PORT}`); console.error("🛠️ NEW: Advanced debugging guide, implementation patterns, UI component library, and GraphQL helper included!"); console.error(`📡 MCP endpoint available at: http://${HOST}:${PORT}/`); console.error(`🔧 Tools available: 10 comprehensive DeSo development tools`); }); // Handle server errors httpServer.on('error', (error) => { console.error("HTTP Server error:", error); process.exit(1); }); // Graceful shutdown handling process.on('SIGINT', () => { console.error("DeSo MCP Server shutting down..."); httpServer.close(() => { transport.close?.(); process.exit(0); }); }); process.on('SIGTERM', () => { console.error("DeSo MCP Server shutting down..."); httpServer.close(() => { transport.close?.(); process.exit(0); }); }); } catch (error) { console.error("Failed to start DeSo MCP Server:", error); process.exit(1); } } main();

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/deso-protocol/deso-mcp'

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