getProposalSentiment
Analyze sentiment for DAO proposals using Discord and Twitter data to gauge community reactions and inform decision-making.
Instructions
Get sentiment analysis for a specific proposal based on Discord and Twitter data
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| proposalId | Yes | The ID of the proposal to analyze |
Implementation Reference
- src/mcp/server/index.ts:289-324 (handler)The MCP tool handler function that executes the tool logic: calls the API client to get sentiment analysis, handles success/error responses, and formats the output as MCP-standard content blocks.async ({ proposalId }) => { try { const response = await apiClient.getProposalSentimentAnalysis(proposalId); if (!response.success) { return { content: [ { type: "text" as const, text: response.error || 'Unknown error', }, ], isError: true, }; } return { content: [ { type: "text" as const, text: JSON.stringify(response.data, null, 2), }, ], }; } catch (error: any) { return { content: [ { type: "text" as const, text: `Error fetching sentiment analysis for proposal: ${error.message || 'Unknown error'}`, }, ], isError: true, }; } }
- src/mcp/server/index.ts:286-288 (schema)Zod schema for input validation of the tool parameters (proposalId: string).{ proposalId: z.string().describe("The ID of the proposal to analyze"), },
- src/mcp/server/index.ts:283-325 (registration)Registration of the 'getProposalSentiment' tool with the MCP server using server.tool().server.tool( "getProposalSentiment", "Get sentiment analysis for a specific proposal based on Discord and Twitter data", { proposalId: z.string().describe("The ID of the proposal to analyze"), }, async ({ proposalId }) => { try { const response = await apiClient.getProposalSentimentAnalysis(proposalId); if (!response.success) { return { content: [ { type: "text" as const, text: response.error || 'Unknown error', }, ], isError: true, }; } return { content: [ { type: "text" as const, text: JSON.stringify(response.data, null, 2), }, ], }; } catch (error: any) { return { content: [ { type: "text" as const, text: `Error fetching sentiment analysis for proposal: ${error.message || 'Unknown error'}`, }, ], isError: true, }; } } );
- Core helper function implementing the sentiment analysis logic: fetches messages from Discord and Twitter (with fallback data), analyzes sentiment using the 'sentiment' library, categorizes concerns, and returns structured SentimentAnalysis data.export async function getProposalSentimentAnalysis(proposalId: string): Promise<SentimentAnalysis> { try { // Fetch data from Discord and Twitter const discordMessages = await fetchDiscordMessages(proposalId); const twitterTweets = await fetchTwitterTweets(proposalId); // Combine all text for analysis const allTexts = [...discordMessages, ...twitterTweets]; // Analyze sentiment const { score, analysis } = analyzeSentiment(allTexts); // Create categories array from the scores const categories = Object.entries(analysis.categoryScores).map(([name, score]) => ({ name, score: score as number })); // Sort categories by score (descending) categories.sort((a, b) => b.score - a.score); // Format the response const sentimentAnalysis: SentimentAnalysis = { proposalId, sentimentScore: score, primaryCategory: categories[0].name, categories, summary: `The proposal to launch a new Horizon token for the Aave ecosystem has faced significant backlash from the community. The main concerns revolve around the perceived dilution of the existing AAVE token's value and governance power, the perceived unfairness of the proposed revenue-sharing model, and the introduction of a permissioned framework that contradicts Aave's decentralized ethos. Many community members feel that the proposal prioritizes institutional needs over the interests of retail users and could create a competing entity that undermines the core Aave protocol. There is a strong desire to maintain the AAVE token as the primary governance and utility token for the ecosystem. While some acknowledge the potential benefits of the Horizon initiative, the overall sentiment is negative towards the proposal in its current form. Suggestions include exploring ways to leverage the existing AAVE token and infrastructure, revising the revenue-sharing model, ensuring the Aave DAO retains significant control, and improving communication and transparency with the community. The community appears open to alternative proposals that better align with Aave's decentralized principles and the long-term interests of AAVE holders, but there is a clear rejection of the current proposal as it stands.`, keyPoints: analysis.keyPoints.length > 0 ? analysis.keyPoints : [ "The proposed token launch is seen as unnecessary and potentially harmful to the Aave token and community.", "The revenue-sharing model is perceived as frontloaded and unfair, favoring early years when adoption and revenue may be low.", "There is a desire to maintain the Aave token as the primary governance and utility token for the ecosystem." ], concerns: analysis.concerns.length > 0 ? analysis.concerns : [ "Dilution of the Aave token's value and attention.", "Misalignment of incentives with the proposed revenue-sharing model.", "Creation of a separate entity that could compete with the Aave ecosystem.", "Lack of transparency and community involvement in the decision-making process." ], sources: { discord: discordMessages.length > 0, twitter: twitterTweets.length > 0 } }; return sentimentAnalysis; } catch (error) { console.error(`Error analyzing sentiment for proposal ${proposalId}:`, error); throw error; } }
- src/mcp/common/api.ts:145-161 (helper)API client method that wraps the sentiment service call and formats the result as a standard Response object with success/data or error.async getProposalSentimentAnalysis(proposalId: string): Promise<Response> { try { // In a production setting, this would call an API endpoint // For now, we'll directly use our sentiment analysis service const sentimentAnalysis = await getProposalSentimentAnalysis(proposalId); return { success: true, data: sentimentAnalysis }; } catch (error: any) { return { success: false, error: error.message || `Failed to get sentiment analysis for proposal: ${proposalId}` }; } }