๐Ÿ“ฆ Kong / volcano-sdk

๐Ÿ“„ server.ts ยท 111 lines
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`);
});