@mastra/mcp 0.11.3-alpha.1 → 0.11.3-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/package.json +16 -3
- package/.turbo/turbo-build.log +0 -4
- package/eslint.config.js +0 -11
- package/integration-tests/node_modules/.bin/tsc +0 -21
- package/integration-tests/node_modules/.bin/tsserver +0 -21
- package/integration-tests/node_modules/.bin/vitest +0 -21
- package/integration-tests/package.json +0 -29
- package/integration-tests/src/mastra/agents/weather.ts +0 -34
- package/integration-tests/src/mastra/index.ts +0 -15
- package/integration-tests/src/mastra/mcp/index.ts +0 -46
- package/integration-tests/src/mastra/tools/weather.ts +0 -13
- package/integration-tests/src/server.test.ts +0 -238
- package/integration-tests/tsconfig.json +0 -13
- package/integration-tests/vitest.config.ts +0 -14
- package/src/__fixtures__/fire-crawl-complex-schema.ts +0 -1013
- package/src/__fixtures__/server-weather.ts +0 -16
- package/src/__fixtures__/stock-price.ts +0 -128
- package/src/__fixtures__/tools.ts +0 -94
- package/src/__fixtures__/weather.ts +0 -269
- package/src/client/client.test.ts +0 -585
- package/src/client/client.ts +0 -628
- package/src/client/configuration.test.ts +0 -856
- package/src/client/configuration.ts +0 -468
- package/src/client/elicitationActions.ts +0 -26
- package/src/client/index.ts +0 -3
- package/src/client/promptActions.ts +0 -70
- package/src/client/resourceActions.ts +0 -119
- package/src/index.ts +0 -2
- package/src/server/index.ts +0 -2
- package/src/server/promptActions.ts +0 -48
- package/src/server/resourceActions.ts +0 -90
- package/src/server/server-logging.test.ts +0 -181
- package/src/server/server.test.ts +0 -2142
- package/src/server/server.ts +0 -1445
- package/src/server/types.ts +0 -59
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -8
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { MCPServer } from '../server/server';
|
|
2
|
-
import { weatherTool } from './tools';
|
|
3
|
-
|
|
4
|
-
const server = new MCPServer({
|
|
5
|
-
name: 'My MCP Server',
|
|
6
|
-
version: '1.0.0',
|
|
7
|
-
tools: {
|
|
8
|
-
weatherTool,
|
|
9
|
-
},
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
server.startStdio().catch(error => {
|
|
13
|
-
const errorMessage = 'Fatal error running server';
|
|
14
|
-
console.error(errorMessage, error);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
6
|
-
|
|
7
|
-
const getStockPrice = async (symbol: string) => {
|
|
8
|
-
// Return mock data for testing
|
|
9
|
-
return {
|
|
10
|
-
symbol,
|
|
11
|
-
currentPrice: '150.00',
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const server = new Server(
|
|
16
|
-
{
|
|
17
|
-
name: 'Stock Price Server',
|
|
18
|
-
version: '1.0.0',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
capabilities: {
|
|
22
|
-
tools: {},
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const stockInputSchema = z.object({
|
|
28
|
-
symbol: z.string().describe('Stock symbol'),
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const stockTool = {
|
|
32
|
-
name: 'getStockPrice',
|
|
33
|
-
description: "Fetches the last day's closing stock price for a given symbol",
|
|
34
|
-
execute: async (args: z.infer<typeof stockInputSchema>) => {
|
|
35
|
-
try {
|
|
36
|
-
const priceData = await getStockPrice(args.symbol);
|
|
37
|
-
return {
|
|
38
|
-
content: [
|
|
39
|
-
{
|
|
40
|
-
type: 'text',
|
|
41
|
-
text: JSON.stringify(priceData),
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
isError: false,
|
|
45
|
-
};
|
|
46
|
-
} catch (error) {
|
|
47
|
-
if (error instanceof Error) {
|
|
48
|
-
return {
|
|
49
|
-
content: [
|
|
50
|
-
{
|
|
51
|
-
type: 'text',
|
|
52
|
-
text: `Stock price fetch failed: ${error.message}`,
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
isError: true,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
content: [
|
|
60
|
-
{
|
|
61
|
-
type: 'text',
|
|
62
|
-
text: 'An unknown error occurred.',
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
isError: true,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Set up request handlers
|
|
72
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
73
|
-
tools: [
|
|
74
|
-
{
|
|
75
|
-
name: stockTool.name,
|
|
76
|
-
description: stockTool.description,
|
|
77
|
-
inputSchema: zodToJsonSchema(stockInputSchema),
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
}));
|
|
81
|
-
|
|
82
|
-
server.setRequestHandler(CallToolRequestSchema, async request => {
|
|
83
|
-
try {
|
|
84
|
-
switch (request.params.name) {
|
|
85
|
-
case 'getStockPrice': {
|
|
86
|
-
const args = stockInputSchema.parse(request.params.arguments);
|
|
87
|
-
return await stockTool.execute(args);
|
|
88
|
-
}
|
|
89
|
-
default:
|
|
90
|
-
return {
|
|
91
|
-
content: [
|
|
92
|
-
{
|
|
93
|
-
type: 'text',
|
|
94
|
-
text: `Unknown tool: ${request.params.name}`,
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
isError: true,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (error instanceof z.ZodError) {
|
|
102
|
-
return {
|
|
103
|
-
content: [
|
|
104
|
-
{
|
|
105
|
-
type: 'text',
|
|
106
|
-
text: `Invalid arguments: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`,
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
isError: true,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
content: [
|
|
114
|
-
{
|
|
115
|
-
type: 'text',
|
|
116
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
isError: true,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Start the server
|
|
125
|
-
const transport = new StdioServerTransport();
|
|
126
|
-
await server.connect(transport);
|
|
127
|
-
|
|
128
|
-
export { server };
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { createTool } from '@mastra/core/tools';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
interface GeocodingResponse {
|
|
5
|
-
results: {
|
|
6
|
-
latitude: number;
|
|
7
|
-
longitude: number;
|
|
8
|
-
name: string;
|
|
9
|
-
}[];
|
|
10
|
-
}
|
|
11
|
-
interface WeatherResponse {
|
|
12
|
-
current: {
|
|
13
|
-
time: string;
|
|
14
|
-
temperature_2m: number;
|
|
15
|
-
apparent_temperature: number;
|
|
16
|
-
relative_humidity_2m: number;
|
|
17
|
-
wind_speed_10m: number;
|
|
18
|
-
wind_gusts_10m: number;
|
|
19
|
-
weather_code: number;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const weatherTool = createTool({
|
|
24
|
-
id: 'get-weather',
|
|
25
|
-
description: 'Get current weather for a location',
|
|
26
|
-
inputSchema: z.object({
|
|
27
|
-
location: z.string().describe('City name'),
|
|
28
|
-
}),
|
|
29
|
-
execute: async ({ context }) => {
|
|
30
|
-
console.log('weather tool', context);
|
|
31
|
-
return await getWeather(context.location);
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const getWeather = async (location: string) => {
|
|
36
|
-
const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`;
|
|
37
|
-
const geocodingResponse = await fetch(geocodingUrl);
|
|
38
|
-
const geocodingData = (await geocodingResponse.json()) as GeocodingResponse;
|
|
39
|
-
|
|
40
|
-
if (!geocodingData.results?.[0]) {
|
|
41
|
-
throw new Error(`Location '${location}' not found`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const { latitude, longitude, name } = geocodingData.results[0];
|
|
45
|
-
|
|
46
|
-
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;
|
|
47
|
-
|
|
48
|
-
const response = await fetch(weatherUrl);
|
|
49
|
-
const data = (await response.json()) as WeatherResponse;
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
temperature: data.current.temperature_2m,
|
|
53
|
-
feelsLike: data.current.apparent_temperature,
|
|
54
|
-
humidity: data.current.relative_humidity_2m,
|
|
55
|
-
windSpeed: data.current.wind_speed_10m,
|
|
56
|
-
windGust: data.current.wind_gusts_10m,
|
|
57
|
-
conditions: getWeatherCondition(data.current.weather_code),
|
|
58
|
-
location: name,
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function getWeatherCondition(code: number): string {
|
|
63
|
-
const conditions: Record<number, string> = {
|
|
64
|
-
0: 'Clear sky',
|
|
65
|
-
1: 'Mainly clear',
|
|
66
|
-
2: 'Partly cloudy',
|
|
67
|
-
3: 'Overcast',
|
|
68
|
-
45: 'Foggy',
|
|
69
|
-
48: 'Depositing rime fog',
|
|
70
|
-
51: 'Light drizzle',
|
|
71
|
-
53: 'Moderate drizzle',
|
|
72
|
-
55: 'Dense drizzle',
|
|
73
|
-
56: 'Light freezing drizzle',
|
|
74
|
-
57: 'Dense freezing drizzle',
|
|
75
|
-
61: 'Slight rain',
|
|
76
|
-
63: 'Moderate rain',
|
|
77
|
-
65: 'Heavy rain',
|
|
78
|
-
66: 'Light freezing rain',
|
|
79
|
-
67: 'Heavy freezing rain',
|
|
80
|
-
71: 'Slight snow fall',
|
|
81
|
-
73: 'Moderate snow fall',
|
|
82
|
-
75: 'Heavy snow fall',
|
|
83
|
-
77: 'Snow grains',
|
|
84
|
-
80: 'Slight rain showers',
|
|
85
|
-
81: 'Moderate rain showers',
|
|
86
|
-
82: 'Violent rain showers',
|
|
87
|
-
85: 'Slight snow showers',
|
|
88
|
-
86: 'Heavy snow showers',
|
|
89
|
-
95: 'Thunderstorm',
|
|
90
|
-
96: 'Thunderstorm with slight hail',
|
|
91
|
-
99: 'Thunderstorm with heavy hail',
|
|
92
|
-
};
|
|
93
|
-
return conditions[code] || 'Unknown';
|
|
94
|
-
}
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
-
import { createServer } from 'http';
|
|
3
|
-
import { createTool } from '@mastra/core/tools';
|
|
4
|
-
import type { Prompt, PromptMessage, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import { MCPServer } from '../server/server';
|
|
7
|
-
import type { MCPServerResources, MCPServerResourceContent, MCPServerPrompts } from '../server/types';
|
|
8
|
-
|
|
9
|
-
const getWeather = async (location: string) => {
|
|
10
|
-
// Return mock data for testing
|
|
11
|
-
return {
|
|
12
|
-
temperature: 20,
|
|
13
|
-
feelsLike: 18,
|
|
14
|
-
humidity: 65,
|
|
15
|
-
windSpeed: 10,
|
|
16
|
-
windGust: 15,
|
|
17
|
-
conditions: 'Clear sky',
|
|
18
|
-
location,
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const serverId = 'weather-server-fixture';
|
|
23
|
-
console.log(`[${serverId}] Initializing`);
|
|
24
|
-
|
|
25
|
-
const weatherInputSchema = z.object({
|
|
26
|
-
location: z.string().describe('City name'),
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const weatherToolDefinition = createTool({
|
|
30
|
-
id: 'getWeather',
|
|
31
|
-
description: 'Get current weather for a location',
|
|
32
|
-
inputSchema: weatherInputSchema,
|
|
33
|
-
execute: async ({ context }) => {
|
|
34
|
-
try {
|
|
35
|
-
const weatherData = await getWeather(context.location);
|
|
36
|
-
return {
|
|
37
|
-
content: [
|
|
38
|
-
{
|
|
39
|
-
type: 'text',
|
|
40
|
-
text: JSON.stringify(weatherData),
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
isError: false,
|
|
44
|
-
};
|
|
45
|
-
} catch (error) {
|
|
46
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
-
return {
|
|
48
|
-
content: [
|
|
49
|
-
{
|
|
50
|
-
type: 'text',
|
|
51
|
-
text: `Weather fetch failed: ${message}`,
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
isError: true,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const weatherResourceDefinitions: Resource[] = [
|
|
61
|
-
{
|
|
62
|
-
uri: 'weather://current',
|
|
63
|
-
name: 'Current Weather Data',
|
|
64
|
-
description: 'Real-time weather data for the current location',
|
|
65
|
-
mimeType: 'application/json',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
uri: 'weather://forecast',
|
|
69
|
-
name: 'Weather Forecast',
|
|
70
|
-
description: '5-day weather forecast',
|
|
71
|
-
mimeType: 'application/json',
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
uri: 'weather://historical',
|
|
75
|
-
name: 'Historical Weather Data',
|
|
76
|
-
description: 'Weather data from the past 30 days',
|
|
77
|
-
mimeType: 'application/json',
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
const weatherResourceTemplatesDefinitions: ResourceTemplate[] = [
|
|
82
|
-
{
|
|
83
|
-
uriTemplate: 'weather://custom/{city}/{days}',
|
|
84
|
-
name: 'Custom Weather Forecast',
|
|
85
|
-
description: 'Generates a custom weather forecast for a city and number of days.',
|
|
86
|
-
mimeType: 'application/json',
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
const weatherResourceContents: Record<string, MCPServerResourceContent> = {
|
|
91
|
-
'weather://current': {
|
|
92
|
-
text: JSON.stringify({
|
|
93
|
-
location: 'San Francisco',
|
|
94
|
-
temperature: 18,
|
|
95
|
-
conditions: 'Partly Cloudy',
|
|
96
|
-
humidity: 65,
|
|
97
|
-
windSpeed: 12,
|
|
98
|
-
updated: new Date().toISOString(),
|
|
99
|
-
}),
|
|
100
|
-
},
|
|
101
|
-
'weather://forecast': {
|
|
102
|
-
text: JSON.stringify([
|
|
103
|
-
{ day: 1, high: 19, low: 12, conditions: 'Sunny' },
|
|
104
|
-
{ day: 2, high: 22, low: 14, conditions: 'Clear' },
|
|
105
|
-
{ day: 3, high: 20, low: 13, conditions: 'Partly Cloudy' },
|
|
106
|
-
{ day: 4, high: 18, low: 11, conditions: 'Rain' },
|
|
107
|
-
{ day: 5, high: 17, low: 10, conditions: 'Showers' },
|
|
108
|
-
]),
|
|
109
|
-
},
|
|
110
|
-
'weather://historical': {
|
|
111
|
-
text: JSON.stringify({
|
|
112
|
-
averageHigh: 20,
|
|
113
|
-
averageLow: 12,
|
|
114
|
-
rainDays: 8,
|
|
115
|
-
sunnyDays: 18,
|
|
116
|
-
recordHigh: 28,
|
|
117
|
-
recordLow: 7,
|
|
118
|
-
}),
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const weatherPrompts: Prompt[] = [
|
|
123
|
-
{
|
|
124
|
-
name: 'current',
|
|
125
|
-
version: 'v1',
|
|
126
|
-
description: 'Get current weather for a location',
|
|
127
|
-
mimeType: 'application/json',
|
|
128
|
-
content: JSON.stringify({
|
|
129
|
-
location: 'Current weather for San Francisco',
|
|
130
|
-
}),
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: 'forecast',
|
|
134
|
-
version: 'v1',
|
|
135
|
-
description: 'Get weather forecast for a location',
|
|
136
|
-
mimeType: 'application/json',
|
|
137
|
-
content: JSON.stringify({
|
|
138
|
-
location: 'Forecast for San Francisco',
|
|
139
|
-
}),
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
name: 'historical',
|
|
143
|
-
version: 'v1',
|
|
144
|
-
description: 'Get historical weather data for a location',
|
|
145
|
-
mimeType: 'application/json',
|
|
146
|
-
content: JSON.stringify({
|
|
147
|
-
location: 'Historical weather for San Francisco',
|
|
148
|
-
}),
|
|
149
|
-
},
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
const mcpServerResources: MCPServerResources = {
|
|
153
|
-
listResources: async () => weatherResourceDefinitions,
|
|
154
|
-
getResourceContent: async ({ uri }: { uri: string }) => {
|
|
155
|
-
if (weatherResourceContents[uri]) {
|
|
156
|
-
return weatherResourceContents[uri];
|
|
157
|
-
}
|
|
158
|
-
throw new Error(`Mock resource content not found for ${uri}`);
|
|
159
|
-
},
|
|
160
|
-
resourceTemplates: async () => weatherResourceTemplatesDefinitions,
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const mcpServerPrompts: MCPServerPrompts = {
|
|
164
|
-
listPrompts: async () => weatherPrompts,
|
|
165
|
-
getPromptMessages: async ({ name }: { name: string }): Promise<PromptMessage[]> => {
|
|
166
|
-
const prompt = weatherPrompts.find(p => p.name === name);
|
|
167
|
-
if (!prompt) {
|
|
168
|
-
throw new Error(`Mock prompt not found for ${name}`);
|
|
169
|
-
}
|
|
170
|
-
return [
|
|
171
|
-
{
|
|
172
|
-
role: 'user',
|
|
173
|
-
content: {
|
|
174
|
-
type: 'text',
|
|
175
|
-
text: prompt.content as string,
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
];
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const mcpServer = new MCPServer({
|
|
183
|
-
name: serverId,
|
|
184
|
-
version: '1.0.0',
|
|
185
|
-
tools: {
|
|
186
|
-
getWeather: weatherToolDefinition,
|
|
187
|
-
},
|
|
188
|
-
resources: mcpServerResources,
|
|
189
|
-
prompts: mcpServerPrompts,
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
193
|
-
const url = new URL(req.url || '', `http://${req.headers.host}`);
|
|
194
|
-
const connectionLogPrefix = `[${serverId}] REQ: ${req.method} ${url.pathname}`;
|
|
195
|
-
console.log(connectionLogPrefix);
|
|
196
|
-
|
|
197
|
-
await mcpServer.startSSE({
|
|
198
|
-
url,
|
|
199
|
-
ssePath: '/sse',
|
|
200
|
-
messagePath: '/message',
|
|
201
|
-
req,
|
|
202
|
-
res,
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const PORT = process.env.WEATHER_SERVER_PORT || 60808;
|
|
207
|
-
console.log(`[${serverId}] Starting HTTP server on port ${PORT}`);
|
|
208
|
-
httpServer.listen(PORT, () => {
|
|
209
|
-
console.log(`[${serverId}] Weather server is running on SSE at http://localhost:${PORT}`);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// --- Interval-based Notifications ---
|
|
213
|
-
const NOTIFICATION_INTERVAL_MS = 1500;
|
|
214
|
-
let resourceUpdateCounter = 0;
|
|
215
|
-
|
|
216
|
-
const notificationInterval = setInterval(async () => {
|
|
217
|
-
// Simulate resource update for weather://current
|
|
218
|
-
resourceUpdateCounter++;
|
|
219
|
-
const newCurrentWeatherText = JSON.stringify({
|
|
220
|
-
location: 'San Francisco',
|
|
221
|
-
temperature: 18 + (resourceUpdateCounter % 5), // Vary temperature slightly
|
|
222
|
-
conditions: resourceUpdateCounter % 2 === 0 ? 'Sunny' : 'Partly Cloudy',
|
|
223
|
-
humidity: 65 + (resourceUpdateCounter % 3),
|
|
224
|
-
windSpeed: 12 + (resourceUpdateCounter % 4),
|
|
225
|
-
updated: new Date().toISOString(),
|
|
226
|
-
});
|
|
227
|
-
weatherResourceContents['weather://current'] = { text: newCurrentWeatherText };
|
|
228
|
-
|
|
229
|
-
const updatePrefix = `[${serverId}] IntervalUpdate`;
|
|
230
|
-
try {
|
|
231
|
-
await mcpServer.resources.notifyUpdated({ uri: 'weather://current' });
|
|
232
|
-
} catch (e: any) {
|
|
233
|
-
console.error(`${updatePrefix} - Error sending resourceUpdated for weather://current via MCPServer: ${e.message}`);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Simulate resource list changed (less frequently, e.g., every 3rd interval)
|
|
237
|
-
if (resourceUpdateCounter % 3 === 0) {
|
|
238
|
-
const listChangePrefix = `[${serverId}] IntervalListChange`;
|
|
239
|
-
try {
|
|
240
|
-
await mcpServer.resources.notifyListChanged();
|
|
241
|
-
} catch (e: any) {
|
|
242
|
-
console.error(`${listChangePrefix} - Error sending resourceListChanged via MCPServer: ${e.message}`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}, NOTIFICATION_INTERVAL_MS);
|
|
246
|
-
|
|
247
|
-
const promptNotificationInterval = setInterval(async () => {
|
|
248
|
-
const listChangePrefix = `[${serverId}] IntervalListChange`;
|
|
249
|
-
try {
|
|
250
|
-
await mcpServer.prompts.notifyListChanged();
|
|
251
|
-
} catch (e: any) {
|
|
252
|
-
console.error(`${listChangePrefix} - Error sending promptListChanged via MCPServer: ${e.message}`);
|
|
253
|
-
}
|
|
254
|
-
}, NOTIFICATION_INTERVAL_MS);
|
|
255
|
-
// --- End Interval-based Notifications ---
|
|
256
|
-
|
|
257
|
-
// Handle graceful shutdown
|
|
258
|
-
process.on('SIGINT', async () => {
|
|
259
|
-
console.log('Shutting down weather server...');
|
|
260
|
-
clearInterval(notificationInterval); // Clear the interval
|
|
261
|
-
clearInterval(promptNotificationInterval); // Clear the interval
|
|
262
|
-
await mcpServer.close();
|
|
263
|
-
httpServer.close(() => {
|
|
264
|
-
console.log('Weather server shut down complete');
|
|
265
|
-
process.exit(0);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
export { mcpServer as server };
|