@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.
Files changed (91) hide show
  1. package/.claude/settings.local.json +29 -0
  2. package/CLAUDE.md +71 -0
  3. package/Dockerfile +14 -0
  4. package/LICENSE +21 -0
  5. package/README.md +234 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +9 -0
  8. package/dist/interfaces/config.d.ts +6 -0
  9. package/dist/interfaces/config.js +1 -0
  10. package/dist/interfaces/moon.d.ts +65 -0
  11. package/dist/interfaces/moon.js +35 -0
  12. package/dist/interfaces/noaa.d.ts +334 -0
  13. package/dist/interfaces/noaa.js +98 -0
  14. package/dist/interfaces/parameters.d.ts +18 -0
  15. package/dist/interfaces/parameters.js +5 -0
  16. package/dist/interfaces/sun.d.ts +103 -0
  17. package/dist/interfaces/sun.js +45 -0
  18. package/dist/mcp-server.d.ts +12 -0
  19. package/dist/mcp-server.js +103 -0
  20. package/dist/moon-phase-service.d.ts +122 -0
  21. package/dist/moon-phase-service.js +187 -0
  22. package/dist/noaa-service.d.ts +60 -0
  23. package/dist/noaa-service.js +159 -0
  24. package/dist/schemas/common.d.ts +14 -0
  25. package/dist/schemas/common.js +18 -0
  26. package/dist/schemas/dpapi.d.ts +198 -0
  27. package/dist/schemas/dpapi.js +89 -0
  28. package/dist/server/config.d.ts +9 -0
  29. package/dist/server/config.js +40 -0
  30. package/dist/server/mcp-server.d.ts +12 -0
  31. package/dist/server/mcp-server.js +103 -0
  32. package/dist/services/dpapi-service.d.ts +72 -0
  33. package/dist/services/dpapi-service.js +164 -0
  34. package/dist/services/moon-phase-service.d.ts +34 -0
  35. package/dist/services/moon-phase-service.js +147 -0
  36. package/dist/services/noaa-parameters-service.d.ts +76 -0
  37. package/dist/services/noaa-parameters-service.js +128 -0
  38. package/dist/services/noaa-service.d.ts +52 -0
  39. package/dist/services/noaa-service.js +151 -0
  40. package/dist/services/sun-service.d.ts +60 -0
  41. package/dist/services/sun-service.js +234 -0
  42. package/dist/sun-service.d.ts +184 -0
  43. package/dist/sun-service.js +218 -0
  44. package/dist/tools/derived-product-tools.d.ts +6 -0
  45. package/dist/tools/derived-product-tools.js +168 -0
  46. package/dist/tools/index.d.ts +16 -0
  47. package/dist/tools/index.js +36 -0
  48. package/dist/tools/moon-tools.d.ts +6 -0
  49. package/dist/tools/moon-tools.js +69 -0
  50. package/dist/tools/parameter-tools.d.ts +6 -0
  51. package/dist/tools/parameter-tools.js +77 -0
  52. package/dist/tools/station-tools.d.ts +6 -0
  53. package/dist/tools/station-tools.js +51 -0
  54. package/dist/tools/sun-tools.d.ts +6 -0
  55. package/dist/tools/sun-tools.js +109 -0
  56. package/dist/tools/water-tools.d.ts +6 -0
  57. package/dist/tools/water-tools.js +150 -0
  58. package/dist/types/moon.d.ts +26 -0
  59. package/dist/types/moon.js +14 -0
  60. package/dist/types/sun.d.ts +49 -0
  61. package/dist/types/sun.js +19 -0
  62. package/dist/types.d.ts +337 -0
  63. package/dist/types.js +98 -0
  64. package/package.json +56 -0
  65. package/smithery.yaml +16 -0
  66. package/src/index.ts +13 -0
  67. package/src/interfaces/moon.ts +44 -0
  68. package/src/interfaces/noaa.ts +130 -0
  69. package/src/interfaces/parameters.ts +20 -0
  70. package/src/interfaces/sun.ts +57 -0
  71. package/src/schemas/common.ts +23 -0
  72. package/src/schemas/dpapi.ts +99 -0
  73. package/src/server/config.ts +43 -0
  74. package/src/server/mcp-server.ts +135 -0
  75. package/src/services/dpapi-service.ts +187 -0
  76. package/src/services/moon-phase-service.ts +167 -0
  77. package/src/services/noaa-parameters-service.ts +139 -0
  78. package/src/services/noaa-service.ts +171 -0
  79. package/src/services/sun-service.ts +275 -0
  80. package/src/tools/derived-product-tools.ts +180 -0
  81. package/src/tools/index.ts +40 -0
  82. package/src/tools/moon-tools.ts +79 -0
  83. package/src/tools/parameter-tools.ts +82 -0
  84. package/src/tools/station-tools.ts +57 -0
  85. package/src/tools/sun-tools.ts +120 -0
  86. package/src/tools/water-tools.ts +166 -0
  87. package/src/types/moon.ts +27 -0
  88. package/src/types/sun.ts +51 -0
  89. package/src/types/suncalc.d.ts +110 -0
  90. package/test-dpapi.js +0 -0
  91. 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,6 @@
1
+ import { FastMCP } from 'fastmcp';
2
+ import { DpapiService } from '../services/dpapi-service.js';
3
+ /**
4
+ * Register derived product tools with the MCP server
5
+ */
6
+ export declare function registerDerivedProductTools(server: FastMCP, dpapiService: DpapiService): void;
@@ -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,6 @@
1
+ import { FastMCP } from 'fastmcp';
2
+ import { NoaaService } from '../services/noaa-service.js';
3
+ /**
4
+ * Register station-related tools with the MCP server
5
+ */
6
+ export declare function registerStationTools(server: FastMCP, noaaService: NoaaService): void;
@@ -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
+ }