1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111import express from 'express';
import cors from 'cors';
import { randomUUID } from 'crypto';
import { z } from 'zod';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
const weatherData: Record<string, { temp: number; condition: string; rain: boolean }> = {
'seattle': { temp: 58, condition: 'Rainy', rain: true },
'san francisco': { temp: 65, condition: 'Partly cloudy', rain: false },
'new york': { temp: 72, condition: 'Sunny', rain: false },
'miami': { temp: 85, condition: 'Hot and humid', rain: false },
'chicago': { temp: 68, condition: 'Windy', rain: false }
};
function createWeatherServer() {
const server = new McpServer({ name: 'weather', version: '1.0.0' });
server.tool(
'get_weather',
'Get current weather for a city',
{
city: z.string().describe('City name'),
unit: z.enum(['celsius', 'fahrenheit']).optional()
},
async ({ city, unit = 'fahrenheit' }) => {
const weather = weatherData[city.toLowerCase()] || { temp: 70, condition: 'Unknown', rain: false };
const temp = unit === 'celsius' ? Math.round((weather.temp - 32) * 5 / 9) : weather.temp;
return {
content: [{
type: 'text',
text: JSON.stringify({
city,
temperature: `${temp}ยฐ${unit[0].toUpperCase()}`,
condition: weather.condition,
rain: weather.rain
})
}]
};
}
);
server.tool(
'get_forecast',
'Get 3-day forecast',
{ city: z.string() },
async ({ city }) => {
return {
content: [{
type: 'text',
text: JSON.stringify({
city,
forecast: [
{ day: 'Today', high: 72, low: 58, condition: 'Sunny' },
{ day: 'Tomorrow', high: 75, low: 60, condition: 'Partly cloudy' },
{ day: 'Day after', high: 70, low: 55, condition: 'Cloudy' }
]
})
}]
};
}
);
return server;
}
const app = express();
app.use(express.json());
// Note: This is a local development example server
// In production, configure CORS to only allow specific origins
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'];
app.use(cors({
origin: process.env.NODE_ENV === 'production' ? allowedOrigins : '*',
exposedHeaders: ['Mcp-Session-Id']
}));
const transports = new Map();
app.post('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'];
let transport = sessionId ? transports.get(sessionId) : undefined;
if (!transport) {
if (!isInitializeRequest(req.body)) {
return res.status(400).json({ error: 'Missing initialization' });
}
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sid) => transports.set(sid, transport)
});
await createWeatherServer().connect(transport);
}
await transport.handleRequest(req, res, req.body);
});
app.get('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'];
const transport = sessionId ? transports.get(sessionId) : undefined;
if (!transport) return res.status(400).send('Invalid session');
await transport.handleRequest(req, res);
});
const PORT = 8001;
app.listen(PORT, () => {
console.log(`๐ค๏ธ Weather MCP server running on http://localhost:${PORT}/mcp`);
});