Skip to main content
Glama

Reddit MCP Server

by ozipi
structured-data-example.ts13.4 kB
/** * @file Structured data example tool handler * @module handlers/tools/structured-data-example * * @remarks * This tool demonstrates how MCP tools can return structured data * alongside text content. Structured data enables better integration, * type safety, and programmatic consumption of tool results. * * The structured data pattern: * 1. Tool defines an output schema * 2. Tool returns both text and structured content * 3. Structured content conforms to the output schema * 4. Clients can validate and use the structured data * * @see {@link https://modelcontextprotocol.io/specification/2025-06-18/server/tools | MCP Tools Specification} */ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import type { MCPToolContext } from '../../types/request-context.js'; import { sendOperationNotification } from '../notifications.js'; import { logger } from '../../utils/logger.js'; // Validation will be done by the framework /** * Arguments for the structured data example tool */ interface StructuredDataExampleArgs { /** The type of structured data to return */ dataType: 'user' | 'analytics' | 'weather' | 'product'; /** Optional ID to fetch specific data */ id?: string; /** Whether to include nested data */ includeNested?: boolean; /** Whether to simulate validation errors */ simulateError?: boolean; } /** * Example structured data interfaces */ interface UserData { id: string; username: string; email: string; created: string; profile: { displayName: string; bio?: string; avatarUrl?: string; verified: boolean; }; stats: { posts: number; comments: number; karma: number; }; } interface AnalyticsData { period: string; metrics: { views: number; uniqueVisitors: number; engagementRate: number; averageSessionDuration: number; }; topContent: Array<{ id: string; title: string; views: number; engagement: number; }>; trends: { viewsChange: number; engagementChange: number; }; } interface WeatherData { location: { city: string; country: string; coordinates: { latitude: number; longitude: number; }; }; current: { temperature: number; feelsLike: number; humidity: number; windSpeed: number; conditions: string; icon: string; }; forecast: Array<{ date: string; high: number; low: number; conditions: string; precipitation: number; }>; } interface ProductData { id: string; name: string; description: string; price: { amount: number; currency: string; discount?: number; }; inventory: { inStock: boolean; quantity: number; warehouse: string; }; ratings: { average: number; count: number; distribution: Record<string, number>; }; } /** * Example tool that demonstrates returning structured data * * @remarks * This tool shows how to return properly structured data that * conforms to defined schemas. It demonstrates validation, * nested objects, arrays, and various data types. * * The tool returns both human-readable text and machine-readable * structured data for maximum compatibility. * * @param args - Tool arguments specifying data type to return * @param context - MCP context with session information * @returns Tool response with structured data */ export async function handleStructuredDataExample( args: StructuredDataExampleArgs, context: MCPToolContext ): Promise<CallToolResult> { try { // Input validation is handled by MCP framework // Send notification about the operation await sendOperationNotification( 'structured_data_example', `Demonstrating ${args.dataType} structured data`, context.sessionId ); // Simulate validation error if requested if (args.simulateError) { throw new Error('Simulated validation error: Invalid data format'); } // Generate structured data based on type let structuredData: any; let textDescription: string; switch (args.dataType) { case 'user': const userData: UserData = { id: args.id || 'user_123', username: 'example_user', email: 'user@example.com', created: new Date('2023-01-15').toISOString(), profile: { displayName: 'Example User', bio: args.includeNested ? 'Software developer and Reddit enthusiast' : undefined, avatarUrl: args.includeNested ? 'https://example.com/avatar.jpg' : undefined, verified: true }, stats: { posts: 42, comments: 156, karma: 1337 } }; structuredData = userData; textDescription = `User ${userData.username} (${userData.profile.displayName}) has ${userData.stats.karma} karma from ${userData.stats.posts} posts and ${userData.stats.comments} comments.`; break; case 'analytics': const analyticsData: AnalyticsData = { period: '2024-01', metrics: { views: 15420, uniqueVisitors: 8234, engagementRate: 0.42, averageSessionDuration: 245 }, topContent: args.includeNested ? [ { id: 'post_1', title: 'Getting Started Guide', views: 3201, engagement: 0.56 }, { id: 'post_2', title: 'Advanced Techniques', views: 2150, engagement: 0.48 }, { id: 'post_3', title: 'Common Mistakes', views: 1892, engagement: 0.51 } ] : [], trends: { viewsChange: 0.15, engagementChange: 0.08 } }; structuredData = analyticsData; textDescription = `Analytics for ${analyticsData.period}: ${analyticsData.metrics.views} views, ${analyticsData.metrics.uniqueVisitors} unique visitors, ${(analyticsData.metrics.engagementRate * 100).toFixed(1)}% engagement rate.`; break; case 'weather': const weatherData: WeatherData = { location: { city: 'San Francisco', country: 'USA', coordinates: { latitude: 37.7749, longitude: -122.4194 } }, current: { temperature: 18, feelsLike: 16, humidity: 72, windSpeed: 15, conditions: 'Partly Cloudy', icon: '⛅' }, forecast: args.includeNested ? [ { date: '2024-01-21', high: 20, low: 14, conditions: 'Sunny', precipitation: 0 }, { date: '2024-01-22', high: 19, low: 13, conditions: 'Cloudy', precipitation: 10 }, { date: '2024-01-23', high: 17, low: 12, conditions: 'Rainy', precipitation: 80 } ] : [] }; structuredData = weatherData; textDescription = `Weather in ${weatherData.location.city}: ${weatherData.current.temperature}°C, ${weatherData.current.conditions}, ${weatherData.current.humidity}% humidity.`; break; case 'product': const productData: ProductData = { id: args.id || 'prod_789', name: 'Premium Widget Pro', description: 'High-quality widget with advanced features', price: { amount: 99.99, currency: 'USD', discount: args.includeNested ? 15 : undefined }, inventory: { inStock: true, quantity: 250, warehouse: 'Warehouse A' }, ratings: { average: 4.5, count: 328, distribution: args.includeNested ? { '5': 198, '4': 87, '3': 28, '2': 10, '1': 5 } : {} } }; structuredData = productData; const finalPrice = productData.price.discount ? productData.price.amount * (1 - productData.price.discount / 100) : productData.price.amount; textDescription = `${productData.name}: ${productData.price.currency} ${finalPrice.toFixed(2)}${productData.price.discount ? ` (${productData.price.discount}% off)` : ''}, ${productData.ratings.average}/5 stars (${productData.ratings.count} reviews), ${productData.inventory.inStock ? 'In Stock' : 'Out of Stock'}.`; break; } // Return both text and structured content return { content: [ { type: 'text', text: textDescription } ], // This is the key feature - returning structured data structuredContent: structuredData }; } catch (error) { logger.error('❌ Structured data example failed', { error: error instanceof Error ? error.message : String(error), dataType: args.dataType }); await sendOperationNotification( 'structured_data_example', `Structured data example failed: ${error instanceof Error ? error.message : 'Unknown error'}`, context.sessionId ); throw error; } } /** * Get the output schema for this tool * * @remarks * This function returns the JSON schema that describes the * structured output of this tool. Clients can use this to * validate the structured content. * * @param dataType - The type of data schema to return * @returns JSON schema for the output */ export function getStructuredDataOutputSchema(dataType: string): any { const schemas: Record<string, any> = { user: { type: 'object', properties: { id: { type: 'string' }, username: { type: 'string' }, email: { type: 'string' }, created: { type: 'string', format: 'date-time' }, profile: { type: 'object', properties: { displayName: { type: 'string' }, bio: { type: 'string' }, avatarUrl: { type: 'string', format: 'uri' }, verified: { type: 'boolean' } }, required: ['displayName', 'verified'] }, stats: { type: 'object', properties: { posts: { type: 'number' }, comments: { type: 'number' }, karma: { type: 'number' } }, required: ['posts', 'comments', 'karma'] } }, required: ['id', 'username', 'email', 'created', 'profile', 'stats'] }, analytics: { type: 'object', properties: { period: { type: 'string' }, metrics: { type: 'object', properties: { views: { type: 'number' }, uniqueVisitors: { type: 'number' }, engagementRate: { type: 'number', minimum: 0, maximum: 1 }, averageSessionDuration: { type: 'number' } } }, topContent: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, title: { type: 'string' }, views: { type: 'number' }, engagement: { type: 'number' } } } }, trends: { type: 'object', properties: { viewsChange: { type: 'number' }, engagementChange: { type: 'number' } } } } }, weather: { type: 'object', properties: { location: { type: 'object', properties: { city: { type: 'string' }, country: { type: 'string' }, coordinates: { type: 'object', properties: { latitude: { type: 'number' }, longitude: { type: 'number' } } } } }, current: { type: 'object', properties: { temperature: { type: 'number' }, feelsLike: { type: 'number' }, humidity: { type: 'number' }, windSpeed: { type: 'number' }, conditions: { type: 'string' }, icon: { type: 'string' } } }, forecast: { type: 'array', items: { type: 'object', properties: { date: { type: 'string' }, high: { type: 'number' }, low: { type: 'number' }, conditions: { type: 'string' }, precipitation: { type: 'number' } } } } } }, product: { type: 'object', properties: { id: { type: 'string' }, name: { type: 'string' }, description: { type: 'string' }, price: { type: 'object', properties: { amount: { type: 'number' }, currency: { type: 'string' }, discount: { type: 'number' } } }, inventory: { type: 'object', properties: { inStock: { type: 'boolean' }, quantity: { type: 'number' }, warehouse: { type: 'string' } } }, ratings: { type: 'object', properties: { average: { type: 'number' }, count: { type: 'number' }, distribution: { type: 'object', additionalProperties: { type: 'number' } } } } } } }; return schemas[dataType] || {}; }

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/ozipi/brainloop-mcp-server-v2'

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