Skip to main content

Examples

This page provides complete, production-ready examples of MCP servers built with the mcp-use/server framework. Each example demonstrates different capabilities and best practices.

DNS Rebinding Protection Example

Use the dedicated DNS rebinding example to understand default permissive behavior and explicit allowedOrigins protection. Example location: libraries/typescript/packages/mcp-use/examples/server/features/dns-rebinding Run from package root:
pnpm run example:server:dns-rebinding
This example also includes copy-paste curl commands to verify:
  • spoofed Host is rejected (403)
  • valid localhost Host is accepted (2xx)

Streaming Tool Props Example

Demonstrates streaming tool arguments to widgets: when an LLM streams tool call arguments, the widget receives partial input in real time via partialToolInput and isStreaming from useWidget, enabling live code or JSON previews as the model generates them. Example location: libraries/typescript/packages/mcp-use/examples/server/features/streaming-props Run from package root (mcp-use package):
pnpm run example:server:streaming-props
Tools include generate-code and generate-json with a code-preview widget that updates live while arguments stream. Test in the MCP Inspector by invoking those tools and watching the widget.

Weather Service Server

A complete weather information server with tools, resources, and UI widgets.
import { MCPServer } from 'mcp-use/server'
import axios from 'axios'

// Types
interface WeatherData {
  temperature: number
  condition: string
  humidity: number
  windSpeed: number
  pressure: number
}

interface Forecast {
  date: string
  high: number
  low: number
  condition: string
  precipitation: number
}

// Create server
const server = new MCPServer({
  name: 'weather-service',
  version: '2.0.0',
  description: 'Comprehensive weather information service'
})

// Mock weather service (replace with real API)
class WeatherService {
  private cache = new Map<string, { data: WeatherData; timestamp: number }>()
  private CACHE_TTL = 5 * 60 * 1000 // 5 minutes

  async getCurrentWeather(city: string): Promise<WeatherData> {
    // Check cache
    const cached = this.cache.get(city)
    if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
      return cached.data
    }

    // Simulate API call (replace with real API)
    const data: WeatherData = {
      temperature: Math.round(50 + Math.random() * 50),
      condition: ['Sunny', 'Cloudy', 'Rainy', 'Partly Cloudy'][Math.floor(Math.random() * 4)],
      humidity: Math.round(40 + Math.random() * 40),
      windSpeed: Math.round(5 + Math.random() * 20),
      pressure: Math.round(1000 + Math.random() * 30)
    }

    // Cache result
    this.cache.set(city, { data, timestamp: Date.now() })
    return data
  }

  async getForecast(city: string, days: number): Promise<Forecast[]> {
    const forecast: Forecast[] = []
    const today = new Date()

    for (let i = 0; i < days; i++) {
      const date = new Date(today)
      date.setDate(date.getDate() + i)

      forecast.push({
        date: date.toISOString().split('T')[0],
        high: Math.round(60 + Math.random() * 30),
        low: Math.round(40 + Math.random() * 20),
        condition: ['Sunny', 'Cloudy', 'Rainy', 'Partly Cloudy'][Math.floor(Math.random() * 4)],
        precipitation: Math.round(Math.random() * 100)
      })
    }

    return forecast
  }
}

const weatherService = new WeatherService()

// Tool: Get current weather
server.tool({
  name: 'get_weather',
  title: 'Get Weather',
  description: 'Get current weather for a city',
  schema: z.object({
    city: z.string().describe('City name'),
    units: z.string().describe('Temperature units (celsius or fahrenheit)').optional().default('fahrenheit')
  }),
}, async ({ city, units = 'fahrenheit' }) => {
    try {
      const weather = await weatherService.getCurrentWeather(city)

      let temp = weather.temperature
      if (units === 'celsius') {
        temp = Math.round((temp - 32) * 5 / 9)
      }

      return text(`Weather in ${city}:\n` +
                `Temperature: ${temp}°${units === 'celsius' ? 'C' : 'F'}\n` +
                `Condition: ${weather.condition}\n` +
                `Humidity: ${weather.humidity}%\n` +
                `Wind Speed: ${weather.windSpeed} mph\n` +
                `Pressure: ${weather.pressure} mb`);
    } catch (error) {
      return error(`Error fetching weather for ${city}: ${error.message}`);
    }
  }
);

// Tool: Get forecast
server.tool({
  name: 'get_forecast',
  title: 'Get Weather Forecast',
  description: 'Get weather forecast for multiple days',
  schema: z.object({
    city: z.string().describe('City name'),
    days: z.number().describe('Number of days (1-7)').optional().default(3)
  }),
}, async ({ city, days = 3 }) => {
    if (days < 1 || days > 7) {
      return error('Please specify days between 1 and 7');
    }

    try {
      const forecast = await weatherService.getForecast(city, days)

      const forecastText = forecast.map(day =>
        `${day.date}: ${day.condition}\n` +
        `  High: ${day.high}°F, Low: ${day.low}°F\n` +
        `  Precipitation: ${day.precipitation}%`
      ).join('\n\n')

      return text(`${days}-day forecast for ${city}:\n\n${forecastText}`);
    } catch (error) {
      return error(`Error fetching forecast: ${error.message}`);
    }
  }
);

// Resource: Current conditions for all monitored cities
server.resource({
  name: 'monitored_cities',
  uri: 'weather://monitored',
  title: 'Monitored Cities Weather',
  description: 'Current weather for all monitored cities',
  mimeType: 'application/json',
  annotations: {
    audience: ['user', 'assistant'],
    priority: 0.8
  },
}, async () => {
    const cities = ['New York', 'London', 'Tokyo', 'Paris', 'Sydney']
    return object(cities.map(city => ({
      city,
      weather: await weatherService.getCurrentWeather(city)
    })));
  }
);

// Resource Template: City-specific weather
server.resourceTemplate({
  name: 'city_weather',
  resourceTemplate: {
    uriTemplate: 'weather://city/{cityName}',
    name: 'City Weather',
    description: 'Weather data for a specific city',
    mimeType: 'application/json'
  },
}, async (uri, params) => {
    try {
      const weather = await weatherService.getCurrentWeather(params.cityName)
      const forecast = await weatherService.getForecast(params.cityName, 3)

      return object({
        city: params.cityName,
        current: weather,
        forecast,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      return error(`Error: Unable to fetch weather for ${params.cityName}`);
    }
  }
);

// Prompt: Weather report generation
server.prompt({
  name: 'weather_report',
  title: 'Weather Report Prompt',
  description: 'Generate a weather report prompt',
  schema: z.object({
    city: z.string().describe('City to display'),
    style: z.string().describe('Style of the report').optional().default('professional')
  }),
}, async ({ city, style = 'professional' }) => {
    const weather = await weatherService.getCurrentWeather(city)
    const forecast = await weatherService.getForecast(city, 3)

    const styleInstructions = {
      professional: 'Use a professional, meteorological tone',
      casual: 'Use a friendly, conversational tone',
      brief: 'Be concise and to the point'
    };

    return {
      messages: [
        {
          role: 'system',
          content: `You are a weather reporter. ${styleInstructions[style] || styleInstructions.professional}.`
        },
        {
          role: 'user',
          content: `Please provide a weather report for ${city}. Current conditions: ${JSON.stringify(weather)}. 3-day forecast: ${JSON.stringify(forecast)}. Include current conditions, forecast summary, and any weather advisories.`
        }
      ]
    }
  }
)

// UI Widget: Weather Dashboard
server.uiResource({
  type: 'externalUrl',
  name: 'weather_dashboard',
  widget: 'weather-dashboard',
  title: 'Weather Dashboard',
  description: 'Interactive weather visualization',
  schema: z.object({
    city: z.string().describe('City to display'),
    showForecast: z.boolean().describe('Show forecast section').optional().default(true),
    theme: z.string().describe('Dashboard theme (light or dark)').optional().default('light')
  }),
  size: ['800px', '600px'],
  annotations: {
    audience: ['user'],
    priority: 0.9
  }
})

// API endpoints for widget data
server.get('/api/weather/:city', async (c) => {
  try {
    const city = c.req.param('city')
    const weather = await weatherService.getCurrentWeather(city)
    return c.json(weather)
  } catch (error) {
    return c.json({ error: error.message }, 500)
  }
})

server.get('/api/forecast/:city', async (c) => {
  const city = c.req.param('city')
  const days = parseInt(c.req.query('days') || '3')
  try {
    const forecast = await weatherService.getForecast(city, days)
    return c.json(forecast)
  } catch (error) {
    return c.json({ error: error.message }, 500)
  }
})

// Health check
server.get('/health', (c) => {
  return c.json({
    status: 'healthy',
    service: 'weather-service',
    version: '2.0.0',
    timestamp: new Date().toISOString()
  })
})

// Start server
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000

server.listen(PORT).then(() => {
  console.log(`Weather Service MCP Server running on port ${PORT}`)
  console.log(`MCP endpoint: http://localhost:${PORT}/mcp`)
  console.log(`Inspector: http://localhost:${PORT}/inspector`)
})

Database Management Server

A server for database operations with schema inspection and query execution.
import { MCPServer } from 'mcp-use/server'
import { Pool } from 'pg' // PostgreSQL client
import sqlFormatter from 'sql-formatter'

// Create server
const server = new MCPServer({
  name: 'database-manager',
  version: '1.0.0',
  description: 'Database management and query execution'
})

// Database connection pool
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,
  idleTimeoutMillis: 30000
})

// Tool: Execute SQL query
server.tool({
  name: 'execute_query',
  title: 'Execute SQL Query',
  description: 'Execute a SQL query against the database',
  schema: z.object({
    query: z.string().describe('SQL query to execute'),
    params: z.array(z.string()).describe('Query parameters for prepared statement').optional(),
    limit: z.number().describe('Maximum number of rows to return').optional().default(100)
  }),
}, async ({ query, params = [], limit = 100 }) => {
    // Security: Only allow SELECT queries in this example
    const normalizedQuery = query.trim().toLowerCase()
    if (!normalizedQuery.startsWith('select')) {
      return error('Error: Only SELECT queries are allowed')
    }

    try {
      // Add LIMIT if not present
      let finalQuery = query
      if (!normalizedQuery.includes('limit')) {
        finalQuery += ` LIMIT ${limit}`
      }

      const result = await pool.query(finalQuery, params)

      return text(`Query executed successfully.\nRows returned: ${result.rowCount}\n\n` +
                `Results:\n${JSON.stringify(result.rows, null, 2)}`)
    } catch (error) {
      return error(`Query error: ${error.message}`)
    }
  }
)

// Tool: Get table schema
server.tool({
  name: 'get_schema',
  title: 'Get Table Schema',
  description: 'Get the schema of a database table',
  schema: z.object({
    tableName: z.string().describe('Name of the table'),
  }),
}, async ({ tableName }) => {
    try {
      const query = `
        SELECT
          column_name,
          data_type,
          is_nullable,
          column_default,
          character_maximum_length
        FROM information_schema.columns
        WHERE table_name = $1
        ORDER BY ordinal_position
      `

      const result = await pool.query(query, [tableName])

      if (result.rows.length === 0) {
        return error(`Table '${tableName}' not found`)
      }

      const schema = result.rows.map(col =>
        `${col.column_name}: ${col.data_type}` +
        `${col.character_maximum_length ? `(${col.character_maximum_length})` : ''}` +
        `${col.is_nullable === 'NO' ? ' NOT NULL' : ''}` +
        `${col.column_default ? ` DEFAULT ${col.column_default}` : ''}`
      ).join('\n')

      return text(`Schema for table '${tableName}':\n\n${schema}`)
    } catch (error) {
      return error(`Error fetching schema: ${error.message}`)
    }
  }
)

// Resource: Database statistics
server.resource({
  name: 'db_stats',
  uri: 'db://statistics',
  title: 'Database Statistics',
  description: 'Current database statistics and metrics',
  mimeType: 'application/json',
  annotations: {
    audience: ['assistant'],
    priority: 0.7
  },
}, async () => {
    try {
      const stats = await pool.query(`
        SELECT
          (SELECT count(*) FROM pg_stat_activity) as connections,
          (SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public') as tables,
          (SELECT pg_database_size(current_database())) as database_size
      `)

      const tableStats = await pool.query(`
        SELECT
          tablename,
          n_live_tup as row_count,
          pg_size_pretty(pg_total_relation_size(tablename::regclass)) as size
        FROM pg_stat_user_tables
        ORDER BY n_live_tup DESC
        LIMIT 10
      `)

      return object({
        overview: stats.rows[0],
        topTables: tableStats.rows,
        timestamp: new Date().toISOString()
      })
    } catch (error) {
      return error(`Error fetching statistics: ${error.message}`)
    }
  }
)

// Prompt: Query generation
server.prompt({
  name: 'generate_query',
  title: 'SQL Query Generation',
  description: 'Generate SQL query from natural language',
  schema: z.object({
    description: z.string().describe('Description of the query to generate'),
    tables: z.array(z.string()).describe('Tables to use in the query').optional(),
  }),
}, async ({ description, tables = [] }) => {
    // Get available tables if not provided
    if (tables.length === 0) {
      const result = await pool.query(`
        SELECT tablename
        FROM pg_tables
        WHERE schemaname = 'public'
      `)
      tables = result.rows.map(row => row.tablename)
    }

    return {
      messages: [
        {
          role: 'system',
          content: 'You are a SQL expert. Generate efficient, safe SQL queries based on requirements.'
        },
        {
          role: 'user',
          content: `Generate a SQL query for the following requirement: "${description}"\n\n` +
                   `Available tables: ${tables.join(', ')}\n\n` +
                   `Requirements:\n` +
                   `- Use proper JOIN syntax\n` +
                   `- Include appropriate WHERE clauses\n` +
                   `- Add LIMIT for large result sets\n` +
                   `- Use parameterized queries for any user input`
        }
      ]
    }
  }
)

// UI Widget: Query Builder
server.uiResource({
  type: 'rawHtml',
  name: 'query_builder',
  title: 'SQL Query Builder',
  description: 'Visual SQL query builder',
  htmlString: `
    <!DOCTYPE html>
    <html>
    <head>
      <style>
        body { font-family: monospace; padding: 20px; }
        .query-area { width: 100%; height: 200px; font-family: monospace; }
        .results { margin-top: 20px; padding: 10px; background: #f5f5f5; }
        button { padding: 10px 20px; background: #007bff; color: white; }
      </style>
    </head>
    <body>
      <h2>SQL Query Builder</h2>
      <textarea class="query-area" id="query" placeholder="Enter SQL query...">
SELECT * FROM users LIMIT 10;
      </textarea>
      <br><br>
      <button onclick="executeQuery()">Execute Query</button>
      <div class="results" id="results"></div>

      <script>
        async function executeQuery() {
          const query = document.getElementById('query').value;
          const results = document.getElementById('results');

          results.innerHTML = 'Executing...';

          try {
            const response = await fetch('/api/query', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ query })
            });

            const data = await response.json();
            results.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
          } catch (error) {
            results.innerHTML = 'Error: ' + error.message;
          }
        }
      </script>
    </body>
    </html>
  `,
  size: ['700px', '500px']
})

// API endpoint for query execution
server.post('/api/query', async (c) => {
  const { query } = await c.req.json()

  // Security: Only allow SELECT
  if (!query.trim().toLowerCase().startsWith('select')) {
    return c.json({ error: 'Only SELECT queries allowed' }, 400)
  }

  try {
    const result = await pool.query(query)
    return c.json({
      rowCount: result.rowCount,
      rows: result.rows
    })
  } catch (error) {
    return c.json({ error: error.message }, 500)
  }
})

// Start server
server.listen(3000)

File System Manager

A server for browsing and managing files with content preview.
import { MCPServer } from 'mcp-use/server'
import fs from 'fs/promises'
import path from 'path'
import mime from 'mime-types'

const server = new MCPServer({
  name: 'file-manager',
  version: '1.0.0',
  description: 'File system browsing and management'
})

// Configuration
const BASE_DIR = process.env.BASE_DIR || process.cwd()
const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB

// Helper: Validate path is within base directory
function validatePath(filePath: string): string {
  const resolved = path.resolve(BASE_DIR, filePath)
  if (!resolved.startsWith(BASE_DIR)) {
    throw new Error('Access denied: Path outside base directory')
  }
  return resolved
}

// Tool: List directory contents
server.tool({
  name: 'list_directory',
  description: 'List contents of a directory',
  schema: z.object({
    path: z.string().describe('Directory path relative to base').optional().default('.'),
    showHidden: z.boolean().describe('Show hidden files').optional().default(false),
  }),
}, async ({ path: dirPath = '.', showHidden = false }) => {   
    try {
      const fullPath = validatePath(dirPath)
      const items = await fs.readdir(fullPath, { withFileTypes: true })
      return text(`Contents of ${dirPath}:\n\n${items.join('\n')}`)
    } catch (error) {
      return error(`Error listing directory: ${error.message}`)
    }
  }
)

// Tool: Read file content
server.tool({
  name: 'read_file',
  description: 'Read the contents of a file',
  schema: z.object({
    path: z.string().describe('File path relative to base'),
    encoding: z.string().describe('File encoding (utf8, base64, etc)').optional().default('utf8'),
  }),
}, async ({ path: filePath, encoding = 'utf8' }) => {
    try {
      const fullPath = validatePath(filePath)
      const stats = await fs.stat(fullPath)

      if (stats.size > MAX_FILE_SIZE) {
        return error(`File too large (${stats.size} bytes). Maximum size: ${MAX_FILE_SIZE} bytes`)
      }

      const content = await fs.readFile(fullPath, encoding as any)
      const mimeType = mime.lookup(fullPath) || 'text/plain'

      return text(`File: ${filePath}\nType: ${mimeType}\nSize: ${stats.size} bytes\n\n---\n\n${content}`)
    } catch (error) {
      return error(`Error reading file: ${error.message}`)
    }
  }
)

// Resource Template: File access
server.resourceTemplate({
  name: 'file',
  resourceTemplate: {
    uriTemplate: 'file://{path}',
    name: 'File Content',
    description: 'Access file contents'
  },
}, async (uri, params) => {
    try {
      const fullPath = validatePath(params.path)
      const stats = await fs.stat(fullPath)
      const mimeType = mime.lookup(fullPath) || 'application/octet-stream'

      if (stats.size > MAX_FILE_SIZE) {
        return error(`File too large: ${stats.size} bytes`)
      }

      // Determine how to read based on MIME type
      if (mimeType.startsWith('text/') || mimeType === 'application/json') {
        const content = await fs.readFile(fullPath, 'utf8')
        return text(content)
      } else {
        const content = await fs.readFile(fullPath)
        return binary(content.toString('base64'), mimeType)
      }
    } catch (error) {
      return error(`Error: ${error.message}`)
    }
  }
)

// Start server
await server.listen(3000)

Best Practices Summary

These examples demonstrate:
  1. Proper Error Handling: All operations include try-catch blocks
  2. Input Validation: Parameters are validated before use
  3. Caching: Weather service implements simple caching
  4. Security: Database server restricts queries, file manager validates paths
  5. Resource Management: Database uses connection pooling
  6. Type Safety: Full TypeScript types throughout
  7. Documentation: Clear descriptions for all tools and resources
  8. Performance: Limits on query results and file sizes
  9. UI Integration: Multiple widget types demonstrated
  10. API Design: RESTful endpoints for widget data

Next Steps