@ryancardin/noaa-tides-currents-mcp-server 1.0.0
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/.claude/settings.local.json +29 -0
- package/CLAUDE.md +71 -0
- package/Dockerfile +14 -0
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -0
- package/dist/interfaces/config.d.ts +6 -0
- package/dist/interfaces/config.js +1 -0
- package/dist/interfaces/moon.d.ts +65 -0
- package/dist/interfaces/moon.js +35 -0
- package/dist/interfaces/noaa.d.ts +334 -0
- package/dist/interfaces/noaa.js +98 -0
- package/dist/interfaces/parameters.d.ts +18 -0
- package/dist/interfaces/parameters.js +5 -0
- package/dist/interfaces/sun.d.ts +103 -0
- package/dist/interfaces/sun.js +45 -0
- package/dist/mcp-server.d.ts +12 -0
- package/dist/mcp-server.js +103 -0
- package/dist/moon-phase-service.d.ts +122 -0
- package/dist/moon-phase-service.js +187 -0
- package/dist/noaa-service.d.ts +60 -0
- package/dist/noaa-service.js +159 -0
- package/dist/schemas/common.d.ts +14 -0
- package/dist/schemas/common.js +18 -0
- package/dist/schemas/dpapi.d.ts +198 -0
- package/dist/schemas/dpapi.js +89 -0
- package/dist/server/config.d.ts +9 -0
- package/dist/server/config.js +40 -0
- package/dist/server/mcp-server.d.ts +12 -0
- package/dist/server/mcp-server.js +103 -0
- package/dist/services/dpapi-service.d.ts +72 -0
- package/dist/services/dpapi-service.js +164 -0
- package/dist/services/moon-phase-service.d.ts +34 -0
- package/dist/services/moon-phase-service.js +147 -0
- package/dist/services/noaa-parameters-service.d.ts +76 -0
- package/dist/services/noaa-parameters-service.js +128 -0
- package/dist/services/noaa-service.d.ts +52 -0
- package/dist/services/noaa-service.js +151 -0
- package/dist/services/sun-service.d.ts +60 -0
- package/dist/services/sun-service.js +234 -0
- package/dist/sun-service.d.ts +184 -0
- package/dist/sun-service.js +218 -0
- package/dist/tools/derived-product-tools.d.ts +6 -0
- package/dist/tools/derived-product-tools.js +168 -0
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/moon-tools.d.ts +6 -0
- package/dist/tools/moon-tools.js +69 -0
- package/dist/tools/parameter-tools.d.ts +6 -0
- package/dist/tools/parameter-tools.js +77 -0
- package/dist/tools/station-tools.d.ts +6 -0
- package/dist/tools/station-tools.js +51 -0
- package/dist/tools/sun-tools.d.ts +6 -0
- package/dist/tools/sun-tools.js +109 -0
- package/dist/tools/water-tools.d.ts +6 -0
- package/dist/tools/water-tools.js +150 -0
- package/dist/types/moon.d.ts +26 -0
- package/dist/types/moon.js +14 -0
- package/dist/types/sun.d.ts +49 -0
- package/dist/types/sun.js +19 -0
- package/dist/types.d.ts +337 -0
- package/dist/types.js +98 -0
- package/package.json +56 -0
- package/smithery.yaml +16 -0
- package/src/index.ts +13 -0
- package/src/interfaces/moon.ts +44 -0
- package/src/interfaces/noaa.ts +130 -0
- package/src/interfaces/parameters.ts +20 -0
- package/src/interfaces/sun.ts +57 -0
- package/src/schemas/common.ts +23 -0
- package/src/schemas/dpapi.ts +99 -0
- package/src/server/config.ts +43 -0
- package/src/server/mcp-server.ts +135 -0
- package/src/services/dpapi-service.ts +187 -0
- package/src/services/moon-phase-service.ts +167 -0
- package/src/services/noaa-parameters-service.ts +139 -0
- package/src/services/noaa-service.ts +171 -0
- package/src/services/sun-service.ts +275 -0
- package/src/tools/derived-product-tools.ts +180 -0
- package/src/tools/index.ts +40 -0
- package/src/tools/moon-tools.ts +79 -0
- package/src/tools/parameter-tools.ts +82 -0
- package/src/tools/station-tools.ts +57 -0
- package/src/tools/sun-tools.ts +120 -0
- package/src/tools/water-tools.ts +166 -0
- package/src/types/moon.ts +27 -0
- package/src/types/sun.ts +51 -0
- package/src/types/suncalc.d.ts +110 -0
- package/test-dpapi.js +0 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import SunCalc from 'suncalc';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Sun event types
|
|
5
|
+
*/
|
|
6
|
+
export var SunEventType;
|
|
7
|
+
(function (SunEventType) {
|
|
8
|
+
SunEventType["SUNRISE"] = "sunrise";
|
|
9
|
+
SunEventType["SUNSET"] = "sunset";
|
|
10
|
+
SunEventType["DAWN"] = "dawn";
|
|
11
|
+
SunEventType["DUSK"] = "dusk";
|
|
12
|
+
SunEventType["SOLAR_NOON"] = "solarNoon";
|
|
13
|
+
SunEventType["NIGHT_START"] = "night";
|
|
14
|
+
SunEventType["NIGHT_END"] = "nightEnd";
|
|
15
|
+
SunEventType["GOLDEN_HOUR_START"] = "goldenHourStart";
|
|
16
|
+
SunEventType["GOLDEN_HOUR_END"] = "goldenHourEnd";
|
|
17
|
+
SunEventType["NAUTICAL_DAWN"] = "nauticalDawn";
|
|
18
|
+
SunEventType["NAUTICAL_DUSK"] = "nauticalDusk";
|
|
19
|
+
SunEventType["ASTRONOMICAL_DAWN"] = "astronomicalDawn";
|
|
20
|
+
SunEventType["ASTRONOMICAL_DUSK"] = "astronomicalDusk";
|
|
21
|
+
})(SunEventType || (SunEventType = {}));
|
|
22
|
+
/**
|
|
23
|
+
* Parameters for getting sun times
|
|
24
|
+
*/
|
|
25
|
+
export const SunTimesParamsSchema = z.object({
|
|
26
|
+
date: z.string().optional().describe('Date to get sun times for (YYYY-MM-DD format). Defaults to current date.'),
|
|
27
|
+
latitude: z.number().min(-90).max(90).describe('Latitude for location-specific calculations'),
|
|
28
|
+
longitude: z.number().min(-180).max(180).describe('Longitude for location-specific calculations'),
|
|
29
|
+
format: z.enum(['json', 'text']).optional().describe('Output format (json or text)'),
|
|
30
|
+
timezone: z.string().optional().describe('Timezone for the results. Defaults to UTC.')
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Parameters for getting sun times for a date range
|
|
34
|
+
*/
|
|
35
|
+
export const SunTimesRangeParamsSchema = z.object({
|
|
36
|
+
start_date: z.string().describe('Start date (YYYY-MM-DD format)'),
|
|
37
|
+
end_date: z.string().describe('End date (YYYY-MM-DD format)'),
|
|
38
|
+
latitude: z.number().min(-90).max(90).describe('Latitude for location-specific calculations'),
|
|
39
|
+
longitude: z.number().min(-180).max(180).describe('Longitude for location-specific calculations'),
|
|
40
|
+
format: z.enum(['json', 'text']).optional().describe('Output format (json or text)'),
|
|
41
|
+
timezone: z.string().optional().describe('Timezone for the results. Defaults to UTC.')
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Parameters for getting sun position
|
|
45
|
+
*/
|
|
46
|
+
export const SunPositionParamsSchema = z.object({
|
|
47
|
+
date: z.string().optional().describe('Date to get sun position for (YYYY-MM-DD format). Defaults to current date.'),
|
|
48
|
+
time: z.string().optional().describe('Time to get sun position for (HH:MM:SS format). Defaults to current time.'),
|
|
49
|
+
latitude: z.number().min(-90).max(90).describe('Latitude for location-specific calculations'),
|
|
50
|
+
longitude: z.number().min(-180).max(180).describe('Longitude for location-specific calculations'),
|
|
51
|
+
format: z.enum(['json', 'text']).optional().describe('Output format (json or text)')
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Parameters for finding the next sun event
|
|
55
|
+
*/
|
|
56
|
+
export const NextSunEventParamsSchema = z.object({
|
|
57
|
+
event: z.nativeEnum(SunEventType).describe('Sun event to find'),
|
|
58
|
+
date: z.string().optional().describe('Starting date (YYYY-MM-DD format). Defaults to current date.'),
|
|
59
|
+
latitude: z.number().min(-90).max(90).describe('Latitude for location-specific calculations'),
|
|
60
|
+
longitude: z.number().min(-180).max(180).describe('Longitude for location-specific calculations'),
|
|
61
|
+
count: z.number().positive().optional().describe('Number of occurrences to return. Defaults to 1.'),
|
|
62
|
+
format: z.enum(['json', 'text']).optional().describe('Output format (json or text)'),
|
|
63
|
+
timezone: z.string().optional().describe('Timezone for the results. Defaults to UTC.')
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* Service for sun calculations
|
|
67
|
+
*/
|
|
68
|
+
export class SunService {
|
|
69
|
+
/**
|
|
70
|
+
* Get sun times for a specific date and location
|
|
71
|
+
* @param params Parameters for the request
|
|
72
|
+
* @returns Sun times information
|
|
73
|
+
*/
|
|
74
|
+
getSunTimes(params) {
|
|
75
|
+
const date = params.date ? new Date(params.date) : new Date();
|
|
76
|
+
const { latitude, longitude } = params;
|
|
77
|
+
// Get sun times data
|
|
78
|
+
const sunTimes = SunCalc.getTimes(date, latitude, longitude);
|
|
79
|
+
// Format times or return null if not available
|
|
80
|
+
const formatTime = (time) => {
|
|
81
|
+
if (!time || isNaN(time.getTime()))
|
|
82
|
+
return null;
|
|
83
|
+
if (params.timezone) {
|
|
84
|
+
try {
|
|
85
|
+
return time.toLocaleTimeString('en-US', { timeZone: params.timezone });
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// If timezone is invalid, fall back to ISO string
|
|
89
|
+
console.warn(`Invalid timezone: ${params.timezone}. Using UTC.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return time.toISOString();
|
|
93
|
+
};
|
|
94
|
+
// Calculate day length in minutes
|
|
95
|
+
const sunrise = sunTimes.sunrise;
|
|
96
|
+
const sunset = sunTimes.sunset;
|
|
97
|
+
let dayLength = 0;
|
|
98
|
+
if (sunrise && sunset && !isNaN(sunrise.getTime()) && !isNaN(sunset.getTime())) {
|
|
99
|
+
dayLength = (sunset.getTime() - sunrise.getTime()) / (60 * 1000);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
date: date.toISOString().split('T')[0],
|
|
103
|
+
sunrise: formatTime(sunTimes.sunrise),
|
|
104
|
+
sunset: formatTime(sunTimes.sunset),
|
|
105
|
+
solarNoon: formatTime(sunTimes.solarNoon),
|
|
106
|
+
dawn: formatTime(sunTimes.dawn),
|
|
107
|
+
dusk: formatTime(sunTimes.dusk),
|
|
108
|
+
nightStart: formatTime(sunTimes.night),
|
|
109
|
+
nightEnd: formatTime(sunTimes.nightEnd),
|
|
110
|
+
goldenHourStart: formatTime(sunTimes.goldenHour),
|
|
111
|
+
goldenHourEnd: formatTime(sunTimes.goldenHourEnd),
|
|
112
|
+
nauticalDawn: formatTime(sunTimes.nauticalDawn),
|
|
113
|
+
nauticalDusk: formatTime(sunTimes.nauticalDusk),
|
|
114
|
+
astronomicalDawn: formatTime(sunTimes.astronomicalDawn),
|
|
115
|
+
astronomicalDusk: formatTime(sunTimes.astronomicalDusk),
|
|
116
|
+
dayLength
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get sun times for a date range
|
|
121
|
+
* @param params Parameters for the request
|
|
122
|
+
* @returns Array of sun times information
|
|
123
|
+
*/
|
|
124
|
+
getSunTimesRange(params) {
|
|
125
|
+
const startDate = new Date(params.start_date);
|
|
126
|
+
const endDate = new Date(params.end_date);
|
|
127
|
+
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
|
128
|
+
throw new Error('Invalid date format. Please use YYYY-MM-DD format.');
|
|
129
|
+
}
|
|
130
|
+
if (startDate > endDate) {
|
|
131
|
+
throw new Error('Start date must be before end date.');
|
|
132
|
+
}
|
|
133
|
+
const result = [];
|
|
134
|
+
const currentDate = new Date(startDate);
|
|
135
|
+
while (currentDate <= endDate) {
|
|
136
|
+
result.push(this.getSunTimes({
|
|
137
|
+
date: currentDate.toISOString().split('T')[0],
|
|
138
|
+
latitude: params.latitude,
|
|
139
|
+
longitude: params.longitude,
|
|
140
|
+
timezone: params.timezone
|
|
141
|
+
}));
|
|
142
|
+
// Move to next day
|
|
143
|
+
currentDate.setDate(currentDate.getDate() + 1);
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get sun position for a specific date, time, and location
|
|
149
|
+
* @param params Parameters for the request
|
|
150
|
+
* @returns Sun position information
|
|
151
|
+
*/
|
|
152
|
+
getSunPosition(params) {
|
|
153
|
+
const date = params.date ? new Date(params.date) : new Date();
|
|
154
|
+
// If time is provided, set it on the date object
|
|
155
|
+
if (params.time) {
|
|
156
|
+
const [hours, minutes, seconds] = params.time.split(':').map(Number);
|
|
157
|
+
date.setHours(hours || 0, minutes || 0, seconds || 0);
|
|
158
|
+
}
|
|
159
|
+
const { latitude, longitude } = params;
|
|
160
|
+
// Get sun position data
|
|
161
|
+
const position = SunCalc.getPosition(date, latitude, longitude);
|
|
162
|
+
// Calculate additional astronomical data
|
|
163
|
+
const declination = Math.asin(Math.sin(23.44 * Math.PI / 180) * Math.sin(position.azimuth));
|
|
164
|
+
const rightAscension = Math.atan2(Math.cos(23.44 * Math.PI / 180) * Math.sin(position.azimuth), Math.cos(position.azimuth));
|
|
165
|
+
return {
|
|
166
|
+
date: date.toISOString().split('T')[0],
|
|
167
|
+
time: date.toISOString().split('T')[1].split('.')[0],
|
|
168
|
+
azimuth: position.azimuth * (180 / Math.PI), // Convert to degrees
|
|
169
|
+
altitude: position.altitude * (180 / Math.PI), // Convert to degrees
|
|
170
|
+
declination: declination * (180 / Math.PI), // Convert to degrees
|
|
171
|
+
rightAscension: rightAscension * (180 / Math.PI) // Convert to degrees
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get the next occurrence(s) of a specific sun event
|
|
176
|
+
* @param params Parameters for the request
|
|
177
|
+
* @returns Array of dates for the next occurrences of the specified event
|
|
178
|
+
*/
|
|
179
|
+
getNextSunEvent(params) {
|
|
180
|
+
const startDate = params.date ? new Date(params.date) : new Date();
|
|
181
|
+
const count = params.count || 1;
|
|
182
|
+
const { latitude, longitude, event } = params;
|
|
183
|
+
const results = [];
|
|
184
|
+
let currentDate = new Date(startDate);
|
|
185
|
+
// Find the next occurrences
|
|
186
|
+
while (results.length < count) {
|
|
187
|
+
// Get sun times for the current date
|
|
188
|
+
const sunTimes = SunCalc.getTimes(currentDate, latitude, longitude);
|
|
189
|
+
const eventTime = sunTimes[event];
|
|
190
|
+
// If the event time is valid and in the future
|
|
191
|
+
if (eventTime && !isNaN(eventTime.getTime()) && eventTime > startDate) {
|
|
192
|
+
let timeString = eventTime.toISOString();
|
|
193
|
+
// Format time based on timezone if provided
|
|
194
|
+
if (params.timezone) {
|
|
195
|
+
try {
|
|
196
|
+
timeString = eventTime.toLocaleTimeString('en-US', { timeZone: params.timezone });
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
// If timezone is invalid, fall back to ISO string
|
|
200
|
+
console.warn(`Invalid timezone: ${params.timezone}. Using UTC.`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
results.push({
|
|
204
|
+
date: eventTime.toISOString().split('T')[0],
|
|
205
|
+
time: timeString.split('T')[1]?.split('.')[0] || timeString,
|
|
206
|
+
event
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
// Move to next day
|
|
210
|
+
currentDate.setDate(currentDate.getDate() + 1);
|
|
211
|
+
// Safety check to prevent infinite loops
|
|
212
|
+
if (results.length === 0 && currentDate.getTime() - startDate.getTime() > 366 * 24 * 60 * 60 * 1000) {
|
|
213
|
+
throw new Error('Could not find the specified sun event within a year.');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return results;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { SeaLevelTrendsSchema, ExtremeWaterLevelsSchema, HighTideFloodingDailySchema, HighTideFloodingMonthlySchema, HighTideFloodingSeasonalSchema, HighTideFloodingAnnualSchema, HighTideFloodingProjectionsSchema, HighTideFloodingLikelihoodsSchema, TopTenWaterLevelsSchema } from '../schemas/dpapi.js';
|
|
2
|
+
/**
|
|
3
|
+
* Register derived product tools with the MCP server
|
|
4
|
+
*/
|
|
5
|
+
export function registerDerivedProductTools(server, dpapiService) {
|
|
6
|
+
// Sea Level Trends tool
|
|
7
|
+
server.addTool({
|
|
8
|
+
name: 'get_sea_level_trends',
|
|
9
|
+
description: 'Get sea level trends and error margins for a station',
|
|
10
|
+
parameters: SeaLevelTrendsSchema,
|
|
11
|
+
execute: async (params) => {
|
|
12
|
+
try {
|
|
13
|
+
const result = await dpapiService.getSeaLevelTrends(params);
|
|
14
|
+
return JSON.stringify(result);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof Error) {
|
|
18
|
+
throw new Error(`Failed to get sea level trends: ${error.message}`);
|
|
19
|
+
}
|
|
20
|
+
throw new Error('Failed to get sea level trends');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
// Extreme Water Levels tool
|
|
25
|
+
server.addTool({
|
|
26
|
+
name: 'get_extreme_water_levels',
|
|
27
|
+
description: 'Get extreme water levels and exceedance probabilities for a station',
|
|
28
|
+
parameters: ExtremeWaterLevelsSchema,
|
|
29
|
+
execute: async (params) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = await dpapiService.getExtremeWaterLevels(params);
|
|
32
|
+
return JSON.stringify(result);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error instanceof Error) {
|
|
36
|
+
throw new Error(`Failed to get extreme water levels: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
throw new Error('Failed to get extreme water levels');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// High Tide Flooding Daily tool
|
|
43
|
+
server.addTool({
|
|
44
|
+
name: 'get_high_tide_flooding_daily',
|
|
45
|
+
description: 'Get high tide flooding daily count data for a station',
|
|
46
|
+
parameters: HighTideFloodingDailySchema,
|
|
47
|
+
execute: async (params) => {
|
|
48
|
+
try {
|
|
49
|
+
const result = await dpapiService.getHighTideFloodingDaily(params);
|
|
50
|
+
return JSON.stringify(result);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof Error) {
|
|
54
|
+
throw new Error(`Failed to get high tide flooding daily data: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
throw new Error('Failed to get high tide flooding daily data');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// High Tide Flooding Monthly tool
|
|
61
|
+
server.addTool({
|
|
62
|
+
name: 'get_high_tide_flooding_monthly',
|
|
63
|
+
description: 'Get high tide flooding monthly count data for a station',
|
|
64
|
+
parameters: HighTideFloodingMonthlySchema,
|
|
65
|
+
execute: async (params) => {
|
|
66
|
+
try {
|
|
67
|
+
const result = await dpapiService.getHighTideFloodingMonthly(params);
|
|
68
|
+
return JSON.stringify(result);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof Error) {
|
|
72
|
+
throw new Error(`Failed to get high tide flooding monthly data: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
throw new Error('Failed to get high tide flooding monthly data');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// High Tide Flooding Seasonal tool
|
|
79
|
+
server.addTool({
|
|
80
|
+
name: 'get_high_tide_flooding_seasonal',
|
|
81
|
+
description: 'Get high tide flooding seasonal count data for a station',
|
|
82
|
+
parameters: HighTideFloodingSeasonalSchema,
|
|
83
|
+
execute: async (params) => {
|
|
84
|
+
try {
|
|
85
|
+
const result = await dpapiService.getHighTideFloodingSeasonal(params);
|
|
86
|
+
return JSON.stringify(result);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof Error) {
|
|
90
|
+
throw new Error(`Failed to get high tide flooding seasonal data: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
throw new Error('Failed to get high tide flooding seasonal data');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
// High Tide Flooding Annual tool
|
|
97
|
+
server.addTool({
|
|
98
|
+
name: 'get_high_tide_flooding_annual',
|
|
99
|
+
description: 'Get high tide flooding annual count data for a station',
|
|
100
|
+
parameters: HighTideFloodingAnnualSchema,
|
|
101
|
+
execute: async (params) => {
|
|
102
|
+
try {
|
|
103
|
+
const result = await dpapiService.getHighTideFloodingAnnual(params);
|
|
104
|
+
return JSON.stringify(result);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error instanceof Error) {
|
|
108
|
+
throw new Error(`Failed to get high tide flooding annual data: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
throw new Error('Failed to get high tide flooding annual data');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// High Tide Flooding Projections tool
|
|
115
|
+
server.addTool({
|
|
116
|
+
name: 'get_high_tide_flooding_projections',
|
|
117
|
+
description: 'Get high tide flooding decadal projections for sea level rise scenarios',
|
|
118
|
+
parameters: HighTideFloodingProjectionsSchema,
|
|
119
|
+
execute: async (params) => {
|
|
120
|
+
try {
|
|
121
|
+
const result = await dpapiService.getHighTideFloodingProjections(params);
|
|
122
|
+
return JSON.stringify(result);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error instanceof Error) {
|
|
126
|
+
throw new Error(`Failed to get high tide flooding projections: ${error.message}`);
|
|
127
|
+
}
|
|
128
|
+
throw new Error('Failed to get high tide flooding projections');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// High Tide Flooding Likelihoods tool
|
|
133
|
+
server.addTool({
|
|
134
|
+
name: 'get_high_tide_flooding_likelihoods',
|
|
135
|
+
description: 'Get high tide flooding daily likelihoods for a station',
|
|
136
|
+
parameters: HighTideFloodingLikelihoodsSchema,
|
|
137
|
+
execute: async (params) => {
|
|
138
|
+
try {
|
|
139
|
+
const result = await dpapiService.getHighTideFloodingLikelihoods(params);
|
|
140
|
+
return JSON.stringify(result);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (error instanceof Error) {
|
|
144
|
+
throw new Error(`Failed to get high tide flooding likelihoods: ${error.message}`);
|
|
145
|
+
}
|
|
146
|
+
throw new Error('Failed to get high tide flooding likelihoods');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// Top Ten Water Levels tool
|
|
151
|
+
server.addTool({
|
|
152
|
+
name: 'get_top_ten_water_levels',
|
|
153
|
+
description: 'Get top ten highest or lowest water levels for a station',
|
|
154
|
+
parameters: TopTenWaterLevelsSchema,
|
|
155
|
+
execute: async (params) => {
|
|
156
|
+
try {
|
|
157
|
+
const result = await dpapiService.getTopTenWaterLevels(params);
|
|
158
|
+
return JSON.stringify(result);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (error instanceof Error) {
|
|
162
|
+
throw new Error(`Failed to get top ten water levels: ${error.message}`);
|
|
163
|
+
}
|
|
164
|
+
throw new Error('Failed to get top ten water levels');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp';
|
|
2
|
+
import { NoaaService } from '../services/noaa-service.js';
|
|
3
|
+
import { MoonPhaseService } from '../services/moon-phase-service.js';
|
|
4
|
+
import { SunService } from '../services/sun-service.js';
|
|
5
|
+
import { NoaaParametersService } from '../services/noaa-parameters-service.js';
|
|
6
|
+
import { DpapiService } from '../services/dpapi-service.js';
|
|
7
|
+
/**
|
|
8
|
+
* Register all tools with the MCP server
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerAllTools(server: FastMCP): {
|
|
11
|
+
noaaService: NoaaService;
|
|
12
|
+
moonPhaseService: MoonPhaseService;
|
|
13
|
+
sunService: SunService;
|
|
14
|
+
parametersService: NoaaParametersService;
|
|
15
|
+
dpapiService: DpapiService;
|
|
16
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NoaaService } from '../services/noaa-service.js';
|
|
2
|
+
import { MoonPhaseService } from '../services/moon-phase-service.js';
|
|
3
|
+
import { SunService } from '../services/sun-service.js';
|
|
4
|
+
import { NoaaParametersService } from '../services/noaa-parameters-service.js';
|
|
5
|
+
import { DpapiService } from '../services/dpapi-service.js';
|
|
6
|
+
import { registerWaterTools } from './water-tools.js';
|
|
7
|
+
import { registerStationTools } from './station-tools.js';
|
|
8
|
+
import { registerMoonTools } from './moon-tools.js';
|
|
9
|
+
import { registerSunTools } from './sun-tools.js';
|
|
10
|
+
import { registerParameterTools } from './parameter-tools.js';
|
|
11
|
+
import { registerDerivedProductTools } from './derived-product-tools.js';
|
|
12
|
+
/**
|
|
13
|
+
* Register all tools with the MCP server
|
|
14
|
+
*/
|
|
15
|
+
export function registerAllTools(server) {
|
|
16
|
+
// Create service instances
|
|
17
|
+
const noaaService = new NoaaService();
|
|
18
|
+
const moonPhaseService = new MoonPhaseService();
|
|
19
|
+
const sunService = new SunService();
|
|
20
|
+
const parametersService = new NoaaParametersService();
|
|
21
|
+
const dpapiService = new DpapiService();
|
|
22
|
+
// Register tools by category
|
|
23
|
+
registerWaterTools(server, noaaService);
|
|
24
|
+
registerStationTools(server, noaaService);
|
|
25
|
+
registerMoonTools(server, moonPhaseService);
|
|
26
|
+
registerSunTools(server, sunService);
|
|
27
|
+
registerParameterTools(server, parametersService);
|
|
28
|
+
registerDerivedProductTools(server, dpapiService);
|
|
29
|
+
return {
|
|
30
|
+
noaaService,
|
|
31
|
+
moonPhaseService,
|
|
32
|
+
sunService,
|
|
33
|
+
parametersService,
|
|
34
|
+
dpapiService
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp';
|
|
2
|
+
import { MoonPhaseService } from '../services/moon-phase-service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register moon-related tools with the MCP server
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerMoonTools(server: FastMCP, moonPhaseService: MoonPhaseService): void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { MoonPhaseParamsSchema, MoonPhasesRangeParamsSchema, NextMoonPhaseParamsSchema } from '../interfaces/moon.js';
|
|
2
|
+
/**
|
|
3
|
+
* Register moon-related tools with the MCP server
|
|
4
|
+
*/
|
|
5
|
+
export function registerMoonTools(server, moonPhaseService) {
|
|
6
|
+
// Add moon phase tool
|
|
7
|
+
server.addTool({
|
|
8
|
+
name: 'get_moon_phase',
|
|
9
|
+
description: 'Get moon phase information for a specific date',
|
|
10
|
+
parameters: MoonPhaseParamsSchema,
|
|
11
|
+
execute: async (params) => {
|
|
12
|
+
try {
|
|
13
|
+
const result = moonPhaseService.getMoonPhase(params);
|
|
14
|
+
if (params.format === 'text') {
|
|
15
|
+
return `Moon phase for ${result.date}: ${result.phaseName} (${(result.illumination * 100).toFixed(1)}% illuminated)`;
|
|
16
|
+
}
|
|
17
|
+
return JSON.stringify(result);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error instanceof Error) {
|
|
21
|
+
throw new Error(`Failed to get moon phase: ${error.message}`);
|
|
22
|
+
}
|
|
23
|
+
throw new Error('Failed to get moon phase');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
// Add moon phases range tool
|
|
28
|
+
server.addTool({
|
|
29
|
+
name: 'get_moon_phases_range',
|
|
30
|
+
description: 'Get moon phase information for a date range',
|
|
31
|
+
parameters: MoonPhasesRangeParamsSchema,
|
|
32
|
+
execute: async (params) => {
|
|
33
|
+
try {
|
|
34
|
+
const results = moonPhaseService.getMoonPhasesRange(params);
|
|
35
|
+
if (params.format === 'text') {
|
|
36
|
+
return results.map(result => `${result.date}: ${result.phaseName} (${(result.illumination * 100).toFixed(1)}% illuminated)`).join('\n');
|
|
37
|
+
}
|
|
38
|
+
return JSON.stringify(results);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
throw new Error(`Failed to get moon phases: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
throw new Error('Failed to get moon phases');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// Add next moon phase tool
|
|
49
|
+
server.addTool({
|
|
50
|
+
name: 'get_next_moon_phase',
|
|
51
|
+
description: 'Get the next occurrence(s) of a specific moon phase',
|
|
52
|
+
parameters: NextMoonPhaseParamsSchema,
|
|
53
|
+
execute: async (params) => {
|
|
54
|
+
try {
|
|
55
|
+
const results = moonPhaseService.getNextMoonPhase(params);
|
|
56
|
+
if (params.format === 'text') {
|
|
57
|
+
return results.map(result => `Next ${result.phase}: ${result.date}`).join('\n');
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(results);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof Error) {
|
|
63
|
+
throw new Error(`Failed to get next moon phase: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
throw new Error('Failed to get next moon phase');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp';
|
|
2
|
+
import { NoaaParametersService } from '../services/noaa-parameters-service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register parameter tools with the MCP server
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerParameterTools(server: FastMCP, parametersService: NoaaParametersService): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ParameterSchema } from '../interfaces/parameters.js';
|
|
2
|
+
/**
|
|
3
|
+
* Register parameter tools with the MCP server
|
|
4
|
+
*/
|
|
5
|
+
export function registerParameterTools(server, parametersService) {
|
|
6
|
+
/**
|
|
7
|
+
* Add parameter definitions tool
|
|
8
|
+
*/
|
|
9
|
+
server.addTool({
|
|
10
|
+
name: 'get_parameter_definitions',
|
|
11
|
+
description: 'Get information about valid parameter values for NOAA API requests',
|
|
12
|
+
parameters: ParameterSchema,
|
|
13
|
+
execute: async (params) => {
|
|
14
|
+
try {
|
|
15
|
+
const { parameter } = params;
|
|
16
|
+
// If no parameter specified, return all parameter types and their descriptions
|
|
17
|
+
if (!parameter) {
|
|
18
|
+
return JSON.stringify({
|
|
19
|
+
time_zones: parametersService.getTimeZones(),
|
|
20
|
+
datums: parametersService.getDatums(),
|
|
21
|
+
units: parametersService.getUnits(),
|
|
22
|
+
tide_intervals: parametersService.getTidePredictionIntervals(),
|
|
23
|
+
current_intervals: parametersService.getCurrentPredictionIntervals(),
|
|
24
|
+
velocity_types: parametersService.getVelocityTypes(),
|
|
25
|
+
products: parametersService.getMeteorologicalProducts(),
|
|
26
|
+
station_types: parametersService.getStationTypes(),
|
|
27
|
+
date_formats: parametersService.getDateFormats(),
|
|
28
|
+
output_formats: parametersService.getOutputFormats()
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Return specific parameter information based on the parameter type
|
|
32
|
+
let result;
|
|
33
|
+
switch (parameter) {
|
|
34
|
+
case 'time_zones':
|
|
35
|
+
result = parametersService.getTimeZones();
|
|
36
|
+
break;
|
|
37
|
+
case 'datums':
|
|
38
|
+
result = parametersService.getDatums();
|
|
39
|
+
break;
|
|
40
|
+
case 'units':
|
|
41
|
+
result = parametersService.getUnits();
|
|
42
|
+
break;
|
|
43
|
+
case 'tide_intervals':
|
|
44
|
+
result = parametersService.getTidePredictionIntervals();
|
|
45
|
+
break;
|
|
46
|
+
case 'current_intervals':
|
|
47
|
+
result = parametersService.getCurrentPredictionIntervals();
|
|
48
|
+
break;
|
|
49
|
+
case 'velocity_types':
|
|
50
|
+
result = parametersService.getVelocityTypes();
|
|
51
|
+
break;
|
|
52
|
+
case 'products':
|
|
53
|
+
result = parametersService.getMeteorologicalProducts();
|
|
54
|
+
break;
|
|
55
|
+
case 'station_types':
|
|
56
|
+
result = parametersService.getStationTypes();
|
|
57
|
+
break;
|
|
58
|
+
case 'date_formats':
|
|
59
|
+
result = parametersService.getDateFormats();
|
|
60
|
+
break;
|
|
61
|
+
case 'output_formats':
|
|
62
|
+
result = parametersService.getOutputFormats();
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(`Unknown parameter type: ${parameter}`);
|
|
66
|
+
}
|
|
67
|
+
return JSON.stringify(result);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof Error) {
|
|
71
|
+
throw new Error(`Failed to get parameter definitions: ${error.message}`);
|
|
72
|
+
}
|
|
73
|
+
throw new Error('Failed to get parameter definitions');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { StationSchema, UnitsSchema } from '../schemas/common.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register station-related tools with the MCP server
|
|
5
|
+
*/
|
|
6
|
+
export function registerStationTools(server, noaaService) {
|
|
7
|
+
// Add stations tool
|
|
8
|
+
server.addTool({
|
|
9
|
+
name: 'get_stations',
|
|
10
|
+
description: 'Get list of stations',
|
|
11
|
+
parameters: z.object({
|
|
12
|
+
type: z.string().optional().describe('Station type (waterlevels, currents, etc.)'),
|
|
13
|
+
format: z.enum(['json', 'xml']).optional().describe('Output format (json, xml)'),
|
|
14
|
+
units: UnitsSchema,
|
|
15
|
+
}),
|
|
16
|
+
execute: async (params) => {
|
|
17
|
+
try {
|
|
18
|
+
const result = await noaaService.getStations(params);
|
|
19
|
+
return JSON.stringify(result);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error instanceof Error) {
|
|
23
|
+
throw new Error(`Failed to get stations: ${error.message}`);
|
|
24
|
+
}
|
|
25
|
+
throw new Error('Failed to get stations');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
// Add station details tool
|
|
30
|
+
server.addTool({
|
|
31
|
+
name: 'get_station_details',
|
|
32
|
+
description: 'Get detailed information about a station',
|
|
33
|
+
parameters: z.object({
|
|
34
|
+
station: StationSchema,
|
|
35
|
+
format: z.enum(['json', 'xml']).optional().describe('Output format (json, xml)'),
|
|
36
|
+
units: UnitsSchema,
|
|
37
|
+
}),
|
|
38
|
+
execute: async (params) => {
|
|
39
|
+
try {
|
|
40
|
+
const result = await noaaService.getStationDetails(params);
|
|
41
|
+
return JSON.stringify(result);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof Error) {
|
|
45
|
+
throw new Error(`Failed to get station details: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
throw new Error('Failed to get station details');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|