Examples
This page provides complete, production-ready examples of MCP servers built with themcp-use/server framework. Each example demonstrates different capabilities and best practices.
Weather Service Server
A complete weather information server with tools, resources, and UI widgets.Copy
Ask AI
import { MCPServer } from 'mcp-use/server'
import express from 'express'
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 (req, res) => {
try {
const weather = await weatherService.getCurrentWeather(req.params.city)
res.json(weather)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
server.get('/api/forecast/:city', async (req, res) => {
const days = parseInt(req.query.days as string) || 3
try {
const forecast = await weatherService.getForecast(req.params.city, days)
res.json(forecast)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
// Health check
server.get('/health', (req, res) => {
res.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.Copy
Ask AI
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 (req, res) => {
const { query } = req.body
// Security: Only allow SELECT
if (!query.trim().toLowerCase().startsWith('select')) {
return res.status(400).json({ error: 'Only SELECT queries allowed' })
}
try {
const result = await pool.query(query)
res.json({
rowCount: result.rowCount,
rows: result.rows
})
} catch (error) {
res.status(500).json({ error: error.message })
}
})
// Start server
server.listen(3000)
File System Manager
A server for browsing and managing files with content preview.Copy
Ask AI
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:- Proper Error Handling: All operations include try-catch blocks
- Input Validation: Parameters are validated before use
- Caching: Weather service implements simple caching
- Security: Database server restricts queries, file manager validates paths
- Resource Management: Database uses connection pooling
- Type Safety: Full TypeScript types throughout
- Documentation: Clear descriptions for all tools and resources
- Performance: Limits on query results and file sizes
- UI Integration: Multiple widget types demonstrated
- API Design: RESTful endpoints for widget data
Next Steps
- Getting Started - Set up your first server
- API Reference - Complete API documentation
- Tools Guide - Deep dive into tool development
- UI Widgets - Building interactive widgets